Implement a Consistent API Response Envelope
Wrap every JSON response in a uniform success/error envelope so clients parse results, errors, and pagination without special-casing each endpoint.
Task
Standardize success and error shapes across all REST endpoints with one reusable envelope.
When to use this
Every endpoint returns a different JSON shape, so the frontend needs endpoint-specific parsing and error handling.
Prerequisites
- A controller layer shared by all endpoints
- A serialization helper or middleware hook
Steps
1. Serialize successes as { data, meta } and errors as { error: { code, message } } from one helper.
Expected output: All endpoints emit the same top-level keys.
2. Add middleware converting thrown errors into the error envelope and setting the matching HTTP status.
curl -s https://api.example.com/items | jq '.data'Expected output: Unhandled errors no longer leak stack traces or HTML.
3. Put trace id and pagination cursors under meta only when relevant.
Expected output: Clients read pagination from one stable location.
4. Version the envelope shape and document it in the API contract.
Expected output: Consumers detect shape changes during review.
Troubleshooting
Problem
Frontend still sees raw errors
Solution
Wrap framework-level 404 and 500 handlers in the same middleware, not only thrown exceptions.
Problem
Double wrapping
Solution
Centralize serialization; controllers return plain data, never pre-wrapped responses.
Limitations
- An envelope adds small payload overhead and is unnecessary for streaming or binary responses.
- GraphQL and gRPC have their own conventions and should not adopt the REST envelope.
Context
Standardize success and error shapes across all REST endpoints with one reusable envelope. Every endpoint returns a different JSON shape, so the frontend needs endpoint-specific parsing and error handling.
Steps
- Serialize successes as { data, meta } and errors as { error: { code, message } } from one helper. Expected: All endpoints emit the same top-level keys.
- Add middleware converting thrown errors into the error envelope and setting the matching HTTP status. Expected: Unhandled errors no longer leak stack traces or HTML.
- Put trace id and pagination cursors under meta only when relevant. Expected: Clients read pagination from one stable location.
- Version the envelope shape and document it in the API contract. Expected: Consumers detect shape changes during review.
type ApiResponse<T> =
| { data: T; meta?: Record<string, unknown> }
| { error: { code: string; message: string } };
Verify
- All endpoints emit the same top-level keys.
- Unhandled errors no longer leak stack traces or HTML.
- Clients read pagination from one stable location.
- Consumers detect shape changes during review.
Limits
- An envelope adds small payload overhead and is unnecessary for streaming or binary responses.
- GraphQL and gRPC have their own conventions and should not adopt the REST envelope.