UNPKG

wcz-layout

Version:

110 lines (98 loc) 4.34 kB
--- name: server-functions description: "Use when: creating or modifying TanStack Start server functions, REST routes, middleware, validation, database transactions, external service calls, or permission checks." --- ## Rules - Before generating code, clarify 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. - If permission is unknown, use `"all"`. - 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. Server functions use `.inputValidator()`. - `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/ — databaseMiddleware wcz-layout/middleware/ — authorizationMiddleware, validationMiddleware src/server/db/schemas/ — tables, enums, relations src/lib/schemas/ — Zod schemas (shared between client and server) src/lib/auth/permissions.ts — permission keys ``` ## Examples ```ts // src/server/actions/<feature>.ts export const selectFeatures = createServerFn() .middleware([databaseMiddleware, authorizationMiddleware("all")]) .handler(async ({ context }) => { return await context.db.select().from(featureTable); }); export const insertFeature = createServerFn({ method: "POST" }) .inputValidator(FeatureSchema) .middleware([databaseMiddleware, authorizationMiddleware("admin")]) .handler(async ({ data, context }) => { await context.db.insert(featureTable).values(data); }); export const updateFeature = createServerFn({ method: "POST" }) .inputValidator(FeatureSchema) .middleware([databaseMiddleware, authorizationMiddleware("admin")]) .handler(async ({ data, context }) => { await context.db.update(featureTable).set(data).where(eq(featureTable.id, data.id)); }); export const deleteFeature = createServerFn({ method: "POST" }) .inputValidator(FeatureSchema.pick({ id: true })) .middleware([databaseMiddleware, authorizationMiddleware("admin")]) .handler(async ({ data, context }) => { await context.db.delete(featureTable).where(eq(featureTable.id, data.id)); }); // src/routes/api/<feature>s/index.ts export const Route = createFileRoute("/api/features/")({ server: { middleware: [databaseMiddleware], handlers: ({ createHandlers }) => createHandlers({ GET: { middleware: [authorizationMiddleware("all")], handler: async ({ context }) => { const items = await context.db.select().from(featureTable); return Response.json(items); }, }, POST: { middleware: [validationMiddleware(FeatureSchema), authorizationMiddleware("admin")], handler: async ({ context }) => { const [response] = await context.db .insert(featureTable) .values(context.data) .returning(); return Response.json(response, { status: 201 }); }, }, }), }, }); // src/routes/api/<feature>s/$id.ts export const Route = createFileRoute("/api/features/$id")({ server: { middleware: [databaseMiddleware, authorizationMiddleware("admin")], handlers: ({ createHandlers }) => createHandlers({ PUT: { middleware: [validationMiddleware(FeatureSchema)], handler: async ({ params, context }) => { await context.db .update(featureTable) .set(context.data) .where(eq(featureTable.id, params.id)); return new Response(null, { status: 204 }); }, }, DELETE: async ({ params, context }) => { await context.db.delete(featureTable).where(eq(featureTable.id, params.id)); return new Response(null, { status: 204 }); }, }), }, }); ```