wcz-layout
Version:
115 lines (98 loc) • 4.37 kB
Markdown
---
name: server
description: Create TanStack Start server functions or REST API routes using Drizzle ORM, Zod schemas and middleware.
---
Before generating code, always ask the user whether the feature should be exposed as public REST API routes or implemented as internal TanStack Start server functions (recommended), and what permission should be required for access.
## Rules
- Follow the chain order for server functions: `inputValidator` → `middleware` → `handler`.
- In middleware, always follow this order: `validationMiddleware` → `databaseMiddleware` → `authorizationMiddleware`.
- Use `validationMiddleware` only in REST API routes (handler-level middleware). Server functions use `.inputValidator()` instead.
- If permission is unknown, use `"all"`.
- `databaseMiddleware` wraps the handler in a DB transaction.
- Reuse schemas from `src/lib/schemas/`.
## File Placement
```
src/server/actions/ — server functions
src/routes/api/ — REST API routes
src/server/middleware/ — server middleware
wcz-layout/middleware/ — shared middleware from npm package
src/server/db/schemas/ — tables, enums, relations
src/lib/schemas/ — Zod schemas
src/lib/auth/permissions.ts — permission keys
```
## Examples
```ts
// imports
import { authorizationMiddleware, validationMiddleware } from "wcz-layout/middleware";
import { databaseMiddleware } from "~/server/middleware/databaseMiddleware";
// src/server/actions/books.ts
export const selectBooks = createServerFn()
.middleware([databaseMiddleware, authorizationMiddleware("all")])
.handler(async ({ context }) => {
return await context.db.select().from(bookTable);
});
export const insertBook = createServerFn({ method: "POST" })
.inputValidator(BookSchema)
.middleware([databaseMiddleware, authorizationMiddleware("admin")])
.handler(async ({ data, context }) => {
await context.db.insert(bookTable).values(data);
});
export const updateBook = createServerFn({ method: "POST" })
.inputValidator(BookSchema)
.middleware([databaseMiddleware, authorizationMiddleware("admin")])
.handler(async ({ data, context }) => {
await context.db.update(bookTable).set(data).where(eq(bookTable.id, data.id));
});
export const deleteBook = createServerFn({ method: "POST" })
.inputValidator(BookSchema.pick({ id: true }))
.middleware([databaseMiddleware, authorizationMiddleware("admin")])
.handler(async ({ data, context }) => {
await context.db.delete(bookTable).where(eq(bookTable.id, data.id));
});
// src/routes/api/books/index.ts
export const Route = createFileRoute("/api/books/")({
server: {
middleware: [databaseMiddleware],
handlers: ({ createHandlers }) =>
createHandlers({
GET: {
middleware: [authorizationMiddleware("all")],
handler: async ({ context }) => {
const items = await context.db.select().from(bookTable);
return Response.json(items);
},
},
POST: {
middleware: [validationMiddleware(BookSchema), authorizationMiddleware("admin")],
handler: async ({ context }) => {
const [response] = await context.db.insert(bookTable).values(context.data).returning();
return Response.json(response, { status: 201 });
},
},
}),
},
});
// src/routes/api/books/$id.ts
export const Route = createFileRoute("/api/books/$id")({
server: {
middleware: [databaseMiddleware, authorizationMiddleware("admin")],
handlers: ({ createHandlers }) =>
createHandlers({
PUT: {
middleware: [validationMiddleware(BookSchema)],
handler: async ({ params, context }) => {
await context.db.update(bookTable).set(context.data).where(eq(bookTable.id, params.id));
return new Response(null, { status: 204 });
},
},
DELETE: async ({ params, context }) => {
await context.db.delete(bookTable).where(eq(bookTable.id, params.id));
return new Response(null, { status: 204 });
},
}),
},
});
```
- Generate TanStack DB Collection.