API Design Principles
Guidelines for building consistent, versioned, and well-documented REST and GraphQL APIs that are easy to consume, evolve, and operate. These conventions follow the Microsoft REST API Guidelines, Google AIPs, and the JSON:API specification where applicable.
Resource Modeling
- Use nouns, not verbs for resources (
/users/123/orders, not/getUserOrders?id=123). - Pluralize collection names; identify items by stable IDs (preferably ULID/UUIDv7).
- Express relationships hierarchically only when one resource cannot exist without the other.
- Use kebab-case in URLs and camelCase in JSON bodies.
HTTP Semantics
GETsafe & idempotent ·PUT/DELETEidempotent ·POSTfor creation and non-idempotent actions ·PATCHfor partial updates (RFC 7396 or JSON Patch RFC 6902).- Return correct status codes:
200,201,202,204,400,401,403,404,409,422,429,5xx. - Honor conditional requests with
ETag/If-Matchfor optimistic concurrency. - Support an
Idempotency-Keyheader on unsafe operations to make retries safe.
Versioning
Version on the URL path (/api/v1/...). Breaking changes require a new major version; additive changes ship in place. Deprecate with a Sunset response header and an entry in the changelog.
Request & Response Envelope
Use a consistent shape: data for the payload, error for failures (RFC 9457 Problem Details), and optional meta for pagination, links, and request IDs.
{
"data": { "id": "01H...", "name": "Acme" },
"meta": {
"requestId": "req_01H...",
"page": { "next": "/api/v1/orgs?cursor=abc" }
}
}Error Model (RFC 9457)
HTTP/1.1 422 Unprocessable Entity
Content-Type: application/problem+json
{
"type": "https://errors.example.com/validation",
"title": "Invalid request",
"status": 422,
"detail": "name must be at least 2 characters",
"instance": "/api/v1/orgs",
"errors": [{ "field": "name", "code": "min_length" }]
}Pagination, Filtering, Sorting
- Prefer cursor-based pagination for stable iteration (
?cursor=...&limit=50). - Filtering:
?status=active&createdAfter=2025-01-01. - Sorting:
?sort=-createdAt,name(leading-for descending).
Authentication & Authorization
Use OAuth 2.1 / OpenID Connect with short-lived access tokens (JWT) and rotating refresh tokens. Authorize at the resource level with scopes and, where needed, fine-grained permissions (RBAC or ABAC). Never log bearer tokens or PII.
Reliability
- Rate limiting with
X-RateLimit-Limit/Remaining/Resetand429responses includingRetry-After. - Timeouts on every outbound call; retries with exponential backoff and jitter; circuit breakers for sustained failures.
- Idempotency on writes — store the response keyed by the idempotency key for at least 24 hours.
Documentation & Contracts
- Every public endpoint has an OpenAPI 3.1 definition with at least one example for each response.
- Contract-test consumers and providers in CI (e.g., Pact).
- Publish a machine-readable changelog and a versioned reference site.
GraphQL Notes
- Avoid the N+1 problem with DataLoader-style batching; persist queries in production.
- Use Relay-style cursor pagination (
edges,pageInfo). - Enforce query depth, complexity, and rate limits at the gateway.