← Recipes

阅读中文版本

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.

~30 minapi-designrestconsistency

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

  1. Serialize successes as { data, meta } and errors as { error: { code, message } } from one helper. Expected: All endpoints emit the same top-level keys.
  2. 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.
  3. Put trace id and pagination cursors under meta only when relevant. Expected: Clients read pagination from one stable location.
  4. 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.