Skip to content

Commit 24adeab

Browse files
added updates readme
1 parent 4ce9b5a commit 24adeab

2 files changed

Lines changed: 186 additions & 55 deletions

File tree

README.md

Lines changed: 68 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,10 @@ pnpm dev # Start dev server
2020
```
2121
┌─────────────────────────────────────────────────────────────────┐
2222
│ Applications │
23-
│ ┌──────────────────────┐ ┌──────────────────────
24-
│ │ effect-worker-api │ │ effect-worker-rpc
25-
│ │ (HTTP REST) (RPC JSON)
26-
│ └──────────────────────┘ └──────────────────────
23+
│ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
24+
│ │effect-worker-api │ │ effect-worker-rpctanstack-start │
25+
│ │ (HTTP REST) │ (RPC JSON) (Full-Stack UI) │
26+
│ └──────────────────┘ └──────────────────┘ └──────────────────┘
2727
├─────────────────────────────────────────────────────────────────┤
2828
│ Shared Packages │
2929
│ ┌───────────┐ ┌───────────┐ ┌───────────┐ ┌──────────────┐ │
@@ -110,6 +110,56 @@ pnpm deploy # Deploy to Cloudflare
110110
- `GET /health` - Health check
111111
- `POST /rpc` - RPC endpoint
112112

113+
### `tanstack-start`
114+
Full-stack React application with TanStack Start, featuring Effect-TS integration via middleware.
115+
116+
```bash
117+
cd apps/tanstack-start
118+
pnpm dev # Local dev server (port 3000)
119+
pnpm deploy # Deploy to Cloudflare
120+
```
121+
122+
**Features:**
123+
- TanStack Router (file-based routing)
124+
- TanStack Query (server state management)
125+
- Effect runtime middleware for server functions
126+
- Tailwind CSS v4 + Shadcn/UI components
127+
128+
**Effect Integration Pattern:**
129+
```typescript
130+
// Middleware creates scoped Effect runtime per-request
131+
export const effectRuntimeMiddleware = createMiddleware().server(
132+
async ({ next }) => {
133+
const servicesLayer = Layer.mergeAll(/* your services */)
134+
return Effect.runPromise(
135+
Effect.scoped(
136+
Effect.gen(function* () {
137+
const runtime = yield* Layer.toRuntime(servicesLayer)
138+
const runEffect = <A, E>(effect: Effect.Effect<A, E, Services>) =>
139+
Runtime.runPromise(runtime)(effect)
140+
return yield* Effect.tryPromise({
141+
try: () => next({ context: { env, runEffect } }),
142+
catch: (e) => { throw e }
143+
})
144+
})
145+
)
146+
)
147+
}
148+
)
149+
150+
// Server functions use runEffect to execute Effect programs
151+
export const myFunction = createServerFn()
152+
.middleware([effectRuntimeMiddleware])
153+
.handler(async ({ context }) => {
154+
return context.runEffect(
155+
Effect.gen(function* () {
156+
const db = yield* PgDrizzle
157+
return yield* db.select().from(users)
158+
})
159+
)
160+
})
161+
```
162+
113163
## Core Patterns
114164

115165
### FiberRef Bridge
@@ -191,7 +241,16 @@ effect-worker-mono/
191241
│ │ │ ├── handlers/ # Handler implementations
192242
│ │ │ └── services/ # Middleware implementations
193243
│ │ └── wrangler.jsonc # Cloudflare config
194-
│ └── effect-worker-rpc/ # RPC API
244+
│ ├── effect-worker-rpc/ # RPC API
245+
│ └── tanstack-start/ # Full-stack React app
246+
│ ├── src/
247+
│ │ ├── routes/ # File-based routes
248+
│ │ ├── components/ # React components
249+
│ │ └── server/ # Server-side code
250+
│ │ ├── middleware/ # Effect runtime middleware
251+
│ │ ├── functions/ # Server functions
252+
│ │ └── types.ts # Effect service types
253+
│ └── wrangler.jsonc # Cloudflare config
195254
├── packages/
196255
│ ├── domain/ # Domain types & schemas
197256
│ │ └── src/
@@ -282,6 +341,7 @@ return yield* makeDrizzle(env.HYPERDRIVE.connectionString)
282341
| Framework | Effect-TS |
283342
| HTTP | @effect/platform |
284343
| RPC | @effect/rpc |
344+
| Full-Stack UI | TanStack Start + TanStack Router + TanStack Query |
285345
| Database | Drizzle ORM + PostgreSQL |
286346
| Build | pnpm workspaces + TypeScript |
287347
| Testing | Vitest + @effect/vitest |
@@ -293,6 +353,9 @@ return yield* makeDrizzle(env.HYPERDRIVE.connectionString)
293353
- **@effect/platform** - HTTP server & middleware
294354
- **@effect/rpc** - RPC protocol
295355
- **@effect/sql-drizzle** - Database integration
356+
- **@tanstack/react-start** - Full-stack React framework
357+
- **@tanstack/react-router** - Type-safe file-based routing
358+
- **@tanstack/react-query** - Server state management
296359
- **drizzle-orm** - Type-safe ORM
297360
- **wrangler** - Cloudflare Workers CLI
298361

apps/tanstack-start/README.md

Lines changed: 118 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# TanStack Start on Cloudflare
22

3-
A modern, full-stack React application built with TanStack Start and deployed on Cloudflare Workers. This template showcases server functions, middleware, type-safe data fetching, and seamless integration with Cloudflare's edge computing platform.
3+
A modern, full-stack React application built with TanStack Start and deployed on Cloudflare Workers. This template showcases server functions, middleware, type-safe data fetching, Effect-TS integration, and seamless integration with Cloudflare's edge computing platform.
44

55
[![TanStack Start on Cloudflare](https://img.youtube.com/vi/TWWS_lo4kOA/0.jpg)](https://www.youtube.com/watch?v=TWWS_lo4kOA)
66

@@ -179,85 +179,151 @@ The `<TanStackRouterDevtools />` component is not required so you can remove it
179179
More information on layouts can be found in the [Layouts documentation](https://tanstack.com/router/latest/docs/framework/react/guide/routing-concepts#layouts).
180180

181181

182-
## 🔄 Data Fetching & Server Functions
182+
## 🔄 Effect-TS Integration
183183

184-
This template demonstrates modern server-side patterns with TanStack Start's server functions, middleware, and seamless client integration.
184+
This template includes Effect-TS integration via a middleware pattern that creates a scoped runtime for each request.
185185

186-
### Server Functions with Middleware
186+
### Effect Runtime Middleware
187187

188-
Server functions run exclusively on the server and maintain type safety across network boundaries:
188+
The `effectRuntimeMiddleware` creates a per-request Effect runtime that provides services to all handlers. The runtime is scoped to the request lifecycle, ensuring proper resource cleanup.
189189

190190
```typescript
191-
// src/core/middleware/example-middleware.ts
192-
export const exampleMiddleware = createMiddleware({
193-
type: 'function'
194-
}).server(async ({ next }) => {
195-
console.log('Middleware executing on server');
196-
return next({
197-
context: {
198-
data: 'Context from middleware'
199-
}
200-
});
201-
});
202-
203-
// src/core/functions/example-functions.ts
204-
const ExampleInputSchema = z.object({
205-
exampleKey: z.string().min(1),
206-
});
207-
208-
type ExampleInput = z.infer<typeof ExampleInputSchema>;
209-
210-
const baseFunction = createServerFn().middleware([
211-
exampleMiddleware,
212-
]);
213-
214-
export const exampleFunction = baseFunction
215-
.inputValidator((data: ExampleInput) => ExampleInputSchema.parse(data))
216-
.handler(async (ctx) => {
217-
// Access validated input: ctx.data
218-
// Access middleware context: ctx.context
219-
// Access Cloudflare env: env.MY_VAR
220-
return 'Server response';
221-
});
191+
// src/server/middleware/effect-runtime.ts
192+
export const effectRuntimeMiddleware = createMiddleware().server(
193+
async ({ next }) => {
194+
// Define service layers (add PgDrizzle, custom services, etc.)
195+
const servicesLayer = Layer.empty
196+
197+
return Effect.runPromise(
198+
Effect.scoped(
199+
Effect.gen(function* () {
200+
const runtime = yield* Layer.toRuntime(servicesLayer)
201+
202+
const runEffect = <A, E>(effect: Effect.Effect<A, E, EffectServices>) => {
203+
return Runtime.runPromise(runtime)(effect)
204+
}
205+
206+
return yield* Effect.tryPromise({
207+
try: async () => await next({ context: { env, runEffect } }),
208+
catch: (error) => { throw error }
209+
})
210+
})
211+
)
212+
)
213+
}
214+
)
215+
```
216+
217+
### Server Functions with Effect
218+
219+
Server functions use the middleware to access `runEffect` for executing Effect programs:
220+
221+
```typescript
222+
// src/server/functions/example-effect-function.ts
223+
const effectFunction = createServerFn().middleware([effectRuntimeMiddleware])
224+
225+
export const greetingFunction = effectFunction
226+
.inputValidator(validateWith(GreetingRequestSchema))
227+
.handler(async ({ data, context }) => {
228+
return context.runEffect(
229+
Effect.gen(function* () {
230+
yield* Effect.log(`Processing: ${data.name}`)
231+
232+
// Access services via yield*
233+
// const db = yield* PgDrizzle
234+
235+
return {
236+
message: `Hello, ${data.name}!`,
237+
timestamp: new Date().toISOString()
238+
}
239+
})
240+
)
241+
})
242+
```
243+
244+
### Adding Services (e.g., Database)
245+
246+
To add services like `PgDrizzle` from `@repo/cloudflare`:
247+
248+
1. **Update the middleware** (`src/server/middleware/effect-runtime.ts`):
249+
```typescript
250+
import { PgDrizzle, makeDrizzle } from "@repo/cloudflare"
251+
252+
const dbLayer = Layer.scoped(
253+
PgDrizzle,
254+
makeDrizzle(env.HYPERDRIVE.connectionString)
255+
)
256+
const servicesLayer = Layer.mergeAll(dbLayer)
257+
```
258+
259+
2. **Update the types** (`src/server/types.ts`):
260+
```typescript
261+
import type { PgDrizzle } from "@repo/cloudflare"
262+
263+
export type EffectServices = PgDrizzle // Add more services with |
264+
```
265+
266+
3. **Use in handlers**:
267+
```typescript
268+
return context.runEffect(
269+
Effect.gen(function* () {
270+
const db = yield* PgDrizzle
271+
return yield* db.select().from(users)
272+
})
273+
)
222274
```
223275

224276
### Client Integration with TanStack Query
225277

226-
Server functions integrate seamlessly with TanStack Query for optimal UX:
278+
Server functions integrate seamlessly with TanStack Query:
227279

228280
```tsx
229-
import { useMutation } from '@tanstack/react-query';
230-
import { exampleFunction } from '@/core/functions/example-functions';
281+
import { useMutation } from '@tanstack/react-query'
282+
import { greetingFunction } from '@/server/functions/example-effect-function'
231283

232284
function MyComponent() {
233285
const mutation = useMutation({
234-
mutationFn: exampleFunction,
286+
mutationFn: (input: { name: string }) => greetingFunction({ data: input }),
235287
onSuccess: (data) => console.log('Success:', data),
236288
onError: (error) => console.error('Error:', error),
237-
});
289+
})
238290

239291
return (
240292
<button
241-
onClick={() => mutation.mutate({ exampleKey: 'Hello Server!' })}
293+
onClick={() => mutation.mutate({ name: 'World' })}
242294
disabled={mutation.isPending}
243295
>
244-
{mutation.isPending ? 'Loading...' : 'Call Server Function'}
296+
{mutation.isPending ? 'Loading...' : 'Run Effect'}
245297
</button>
246-
);
298+
)
247299
}
248300
```
249301

250302
### Key Benefits
251303

252-
- **🔒 Type-Safe**: Full TypeScript support with Zod validation
253-
- **🚀 Server-First**: Secure server-side logic with client convenience
254-
- **⚡ Edge Computing**: Runs on Cloudflare's global edge network
255-
- **🔄 Seamless Integration**: Works perfectly with TanStack Query
256-
- **🧩 Composable**: Middleware chains for auth, logging, validation
304+
- **🎯 Type-Safe**: Full TypeScript support with Effect Schema validation
305+
- **🔄 Scoped Runtime**: Per-request Effect runtime with proper resource cleanup
306+
- **⚡ Edge Computing**: Effect programs run on Cloudflare's global edge
307+
- **🧩 Composable Services**: Easily add database, auth, logging services
308+
- **🔒 Server-First**: Secure server-side logic with client convenience
309+
310+
### Project Structure
311+
312+
```
313+
src/server/
314+
├── middleware/
315+
│ ├── effect-runtime.ts # Effect runtime middleware
316+
│ └── index.ts # Middleware exports
317+
├── functions/
318+
│ ├── example-effect-function.ts # Example Effect server function
319+
│ └── index.ts # Function exports
320+
├── types.ts # EffectServices, EffectContext types
321+
└── index.ts # Server exports
322+
```
257323

258324
### Interactive Demo
259325

260-
This template includes a live demo showcasing the middleware and server function patterns. Check your server logs when running the demo to see the execution flow!
326+
This template includes a live demo showcasing the Effect server function patterns. Check your server logs when running the demo to see Effect's structured logging!
261327

262328
## 🧪 Testing
263329

@@ -280,6 +346,7 @@ This template includes the latest and greatest from the React ecosystem:
280346
- **TanStack Start** - Full-stack React framework with SSR
281347
- **React 19** - Latest React with concurrent features
282348
- **TypeScript** - Strict type checking enabled
349+
- **Effect-TS** - Type-safe functional programming and service composition
283350

284351
### **Routing & Data**
285352
- **TanStack Router** - Type-safe, file-based routing
@@ -304,6 +371,7 @@ This template includes the latest and greatest from the React ecosystem:
304371
- **[TanStack Start](https://tanstack.com/start)** - Full-stack React framework
305372
- **[TanStack Router](https://tanstack.com/router)** - Type-safe routing
306373
- **[TanStack Query](https://tanstack.com/query)** - Server state management
374+
- **[Effect-TS](https://effect.website/)** - Type-safe functional effects
307375
- **[Cloudflare Workers](https://workers.cloudflare.com/)** - Edge computing platform
308376
- **[Shadcn/UI](https://ui.shadcn.com/)** - Component library
309377
- **[Tailwind CSS](https://tailwindcss.com/)** - Utility-first CSS

0 commit comments

Comments
 (0)