API Route Style
Conventions for HTTP API endpoints (Next.js Route Handlers and equivalents). Handlers are thin: parse, validate, delegate, map to a status code. Business logic lives in services, not in the route.
Next.js Route Handlers live in app/.../route.ts and export functions named for HTTP verbs (GET, POST, ...) that return a Response. Source: Next.js, https://nextjs.org/docs/app/building-your-application/routing/route-handlers, 2026-05-31
Thin handler, four steps
- Parse the request (params, query, body).
- Validate every input with a Zod schema; on failure return
400with a structured error (see Zod Validation Style). - Delegate to a service or domain function that returns a
Result(see Error-Handling Style). - Map the result to an HTTP status and a typed response body.
No database queries, no business rules, no branching domain logic inside the handler itself. The route is an adapter between HTTP and your service layer. Source: compiled from TypeScript Style, 2026-05-31
Status codes mean what they say
Use the right code, not 200 with an { ok: false } body: 201 for created, 204 for no content, 400 malformed, 401 unauthenticated, 403 unauthorized, 404 missing, 409 conflict, 422 semantically invalid, 429 rate-limited, 500 for unexpected server faults. Return a consistent error envelope, for example { error: { code, message } }, across every endpoint. Source: compiled from wiki, 2026-05-31
Security and trust
- Authenticate and authorize first, before parsing anything expensive. Never trust the client.
- Treat the body as hostile until Zod has parsed it.
- Do not leak internals: map internal errors to safe external messages at the edge (see Dual-Audience Errors). The rich internal detail belongs in logs, the
contextchannel of Depth-of-Context Interfaces, not the response.
Conventions
- Validate and read params/searchParams explicitly; coerce with Zod, do not trust string types.
- Make mutating endpoints idempotent where the protocol allows (accept an idempotency key on
POSTfor payments and similar). - Paginate list endpoints with stable cursor or limit/offset conventions; document the shape.
- Keep response bodies typed end-to-end (infer the type from the Zod output schema).
Route Contract
| Step | Default | Banned pattern | Proof |
|---|---|---|---|
| Auth | Authenticate and authorize before expensive work | Parse/process untrusted body before auth on sensitive routes | Unit/integration test for unauthenticated and unauthorized paths |
| Parse | Zod or typed parser at the boundary | Trusting string params or JSON shape | Invalid body/query test |
| Delegate | Call domain/service function | Business logic inside route handler | Thin handler review |
| Map | Typed result to HTTP status and envelope | 200 with { ok: false } |
Status-code tests for success and failure |
| Observe | Log internal context safely | Leak stack/provider internals to client | Error-shape snapshot or assertion |
Exceptions
A tiny read-only route may inline simple delegation when it has no domain branch, no auth nuance, and no reusable service boundary. The moment the handler has multiple failure modes, extract the service or parser. Server Functions follow the same trust model; see React Server Components Style.
Validation
Every new mutating route needs tests or request-level proof for valid input, invalid input, unauthenticated access, unauthorized access when distinct, and the main domain failure. The route is not done until status codes prove the contract, not merely the happy path.
Proof
API route proof is the public boundary responding with the expected status and body for success, bad input, auth failure, and domain failure. A service-layer unit test cannot prove the route contract by itself.
Style Rule Contract
This page follows Style Rule Contract: name the default pattern, banned pattern, reason, exception, proof, and codification path clearly enough that the next agent can apply the rule without asking Kevin again. If a local repo has a stricter rule, follow the local rule and update the wiki when the decision becomes durable.
Timeline
- 2026-07-01 | Aligned this page with Style Rule Contract so style guidance names default behavior, banned patterns, exceptions, proof, and codification expectations. Source: User request, 2026-07-01
- 2026-07-01 | Added route-contract, exception, and validation sections for auth-first parsing, thin handlers, typed status mapping, and request-level proof. Source: User request, 2026-07-01
- 2026-07-01 | Made API route proof explicit at the public boundary for success, bad input, auth failure, and domain failure. Source: User request, 2026-07-01
- 2026-05-31 | Page created from the Next.js Route Handlers docs and the repo's TypeScript conventions. Captured the thin-handler four-step (parse, validate, delegate, map), correct status-code usage, auth-first security, and consistent error envelopes, cross-linked to Zod Validation Style and Error-Handling Style. Source: https://nextjs.org/docs/app/building-your-application/routing/route-handlers, 2026-05-31