Scaffolding the API layer, I kept getting route handlers that parsed await req.json() with zero validation, returned bare strings on error, and used a different error shape in every file, so the frontend couldn't handle failures generically.
The prompt below makes every route validate input with zod, return one canonical error envelope, and set correct status codes, and it forces a tiny wrapper so I'm not repeating the try/catch boilerplate in twelve files. The frontend now has exactly one error shape to handle.
Open question: do you version the error envelope from day one or add it later?
Scaffold the API route handlers for a Next.js 14 App Router app (app/api/**/route.ts). Consistency and validation are the point.
SHARED FIRST: create a `withRoute` wrapper and canonical response helpers.
- Success envelope: `{ ok: true, data: T }`. Error envelope: `{ ok: false, error: { code: string, message: string, details?: unknown } }`. Every route returns one of these two shapes, always.
- `withRoute` handles: JSON parse, zod validation of body/query/params, method checking, and a single try/catch that maps thrown errors to the error envelope with the right status. No route repeats this boilerplate.
- Status codes: 400 validation, 401 unauth, 403 forbidden, 404 not found, 409 conflict, 422 semantic, 500 unexpected. Map error classes to these; never return 200 with an error body.
PER ROUTE:
- Validate input with a zod schema before touching any logic. Reject unknown fields (.strict()).
- Type the handler so `data` is the parsed, typed input, not `any`.
- Never leak internal error messages or stack traces to the client on 500; log server-side, return a generic message + a code.
DELIVER: lib/api/withRoute.ts, lib/api/responses.ts, and two example routes (one GET with query params, one POST with a body). Strict TS, must build clean. End by showing the exact JSON a validation failure returns.