
api-design
Build production-ready HTTP APIs with clean handlers, consistent error envelopes, health checks, COR
by jagreehal|Open Source
API Design Patterns
Critical rules
- Handlers only translate HTTP ↔ domain: validate at the edge, call
fn(args, deps)/ Result, map to response. No business logic in handlers. - One error envelope everywhere;
ORPCError(code)must matchErrorResponse.code. - Shared domain-error → status mapping;
/healthand/ready;X-Request-IDin middleware once. - Mutations require
Idempotency-Key; 429 includesRetry-After; drain with 503 on shutdown. - One file per route with co-located tests; compose routers via factories.
- Before implementing routes or ops concerns, read references/patterns.md.
Workflow
- Define Zod input/output schemas at the framework boundary.
- Create a route factory
createX({ deps })that delegates to domain functions. - Map domain errors through the shared status table and consistent envelope.
- Add health/ready, CORS, request ID middleware, idempotency, rate limits, shutdown — see references/patterns.md.
- Compose routers at module boundaries (
createPostsRouter→createApiRouter). - Write co-located tests with
call()— see references/testing.md.
Resources
- references/patterns.md — factories, errors, health, CORS, idempotency, organization. Read when designing endpoints.
- references/testing.md — route unit tests with mocked deps. Read when writing route tests.
Validation
- Routes are factories with injected deps; handlers stay thin
- Errors use one envelope; codes match
- Shared status mapping;
/healthand/readyexist - Mutations honor Idempotency-Key; 429 has Retry-After; shutdown → 503
- X-Request-ID set in middleware
- Co-located success and error tests per route
Constraints
- Internal calls and background jobs without HTTP are out of scope. Retry policy belongs in
resilience. - Related:
fn-args-deps,validation-boundary,result-types,resilience,observability.