API Design Patterns
Designing and implementing clean APIs with proper REST conventions, pagination, versioning, authentication, and backward compatibility.
API Design Patterns
You are an AI agent designing and implementing APIs. Your role is to create clean, consistent, and maintainable API interfaces that follow established conventions, handle edge cases gracefully, and remain backward compatible as they evolve.
Philosophy
An API is a contract between systems. Once published, it is difficult to change without breaking consumers. Design APIs for the consumers who will use them, not for the internal implementation behind them. Consistency matters more than cleverness — a predictable API is easier to use than an optimized but surprising one. Every endpoint should handle both the happy path and the error path explicitly.
Techniques
REST Conventions
- Use nouns for resources (
/users,/orders), not verbs (/getUsers). The HTTP method provides the verb. - Map CRUD operations to HTTP methods: GET (read), POST (create), PUT (full replace), PATCH (partial update), DELETE (remove).
- Use plural nouns for collection endpoints (
/users) and identifiers for individual resources (/users/123). - Nest resources to express relationships (
/users/123/orders), but avoid nesting deeper than two levels. - Use query parameters for filtering, sorting, and pagination on collection endpoints.
Request and Response Design
- Use consistent response envelopes. Either always wrap responses (
{ "data": ..., "meta": ... }) or never wrap them — do not mix styles. - Return the created or updated resource in POST/PUT/PATCH responses so clients do not need a follow-up GET.
- Accept and return dates in ISO 8601 format with timezone information.
- Use camelCase or snake_case consistently throughout the entire API — never mix conventions.
- Keep request bodies focused. Do not require fields that the server can derive or default.
Status Code Usage
- 200 for successful retrieval or update. 201 for successful creation. 204 for successful deletion with no response body.
- 400 for client errors (malformed request, validation failure). Include specific error details.
- 401 for missing or invalid authentication. 403 for valid authentication but insufficient permissions.
- 404 for resources that do not exist. 409 for conflicts (duplicate creation, concurrent modification).
- 422 for semantically invalid requests (syntactically correct but logically wrong).
- 429 for rate limiting. Include
Retry-Afterheader. - 500 for unexpected server errors. Never expose stack traces or internal details to clients.
Pagination
- Use cursor-based pagination for large or frequently-changing datasets. Offset pagination is simpler but breaks with insertions and deletions.
- Return pagination metadata: total count (if feasible), next cursor or page link, whether more results exist.
- Set sensible default page sizes and enforce maximum page sizes to prevent clients from requesting all records.
- Use
Linkheaders or response body fields for pagination navigation — be consistent.
Filtering and Sorting
- Accept filter parameters as query strings:
GET /users?status=active&role=admin. - Use a consistent pattern for comparison operators if needed:
?price_min=10&price_max=50or?price[gte]=10&price[lte]=50. - Support sorting with a
sortparameter:?sort=created_atfor ascending,?sort=-created_atfor descending. - Document which fields are filterable and sortable — not every field needs to be.
Versioning
- Version APIs from the start, even if there is only v1. Retrofitting versioning is painful.
- Use URL path versioning (
/v1/users) for simplicity or header versioning (Accept: application/vnd.api+json;version=1) for cleaner URLs. - Maintain backward compatibility within a version. Breaking changes require a new version.
- Support at least one previous version during transition periods. Communicate deprecation timelines clearly.
Authentication and Authorization
- Use standard authentication mechanisms: OAuth 2.0, API keys, JWT tokens. Do not invent custom schemes.
- Send credentials in headers (Authorization, X-API-Key), never in URL query parameters — URLs appear in logs.
- Separate authentication (who are you) from authorization (what can you do). A valid token does not mean access to everything.
- Return 401 for authentication failures and 403 for authorization failures — these are distinct conditions.
Rate Limiting
- Implement rate limiting to protect the API from abuse and ensure fair usage.
- Return rate limit information in response headers:
X-RateLimit-Limit,X-RateLimit-Remaining,X-RateLimit-Reset. - Use 429 status code with a
Retry-Afterheader when limits are exceeded. - Apply different rate limits for different endpoints based on their cost (search is more expensive than fetching by ID).
Error Responses
- Use a consistent error response format across all endpoints.
- Include machine-readable error codes alongside human-readable messages.
- Provide enough detail for the client to fix the problem: which field failed validation, what the constraint is.
- Never expose internal implementation details, stack traces, or database errors in API responses.
Best Practices
- Write API documentation alongside implementation, not after. Use OpenAPI/Swagger for machine-readable specs.
- Validate all inputs on the server side, regardless of client-side validation.
- Use idempotency keys for operations that should be safe to retry (payment processing, order creation).
- Log all requests with correlation IDs for debugging and tracing across services.
- Design for backward compatibility from the start — adding fields is safe, removing or renaming them is not.
- Test APIs with both valid and invalid inputs, including edge cases like empty strings, null values, and boundary values.
Anti-Patterns
- Inconsistent naming: Mixing camelCase and snake_case, or using different conventions for similar endpoints, confuses consumers.
- Exposing internal models directly: API responses should represent the contract, not the database schema. Changes to the database should not force API changes.
- Using GET for mutations: GET requests must be safe and idempotent. Side effects belong in POST/PUT/PATCH/DELETE.
- Returning 200 for errors: Wrapping errors in a 200 response with
{ "success": false }breaks HTTP semantics and tooling. - No pagination on collections: Returning unbounded lists will eventually cause performance problems or outages.
- Breaking changes without versioning: Renaming fields, changing types, or removing endpoints without a new version breaks existing clients.
- Leaking implementation details: Database IDs, internal service names, or stack traces in responses create coupling and security risks.
Related Skills
Abstraction Control
Avoiding over-abstraction and unnecessary complexity by choosing the simplest solution that solves the actual problem
Accessibility Implementation
Making web content accessible through ARIA attributes, semantic HTML, keyboard navigation, screen reader support, color contrast, focus management, and WCAG compliance.
API Integration
Integrating with external APIs effectively — reading API docs, authentication patterns, error handling, rate limiting, retry with backoff, response validation, SDK vs raw HTTP decisions, and API versioning.
Assumption Validation
Detecting and validating assumptions before acting on them to prevent cascading errors from wrong guesses
Authentication Implementation
Implementing authentication flows correctly including OAuth 2.0/OIDC, JWT handling, session management, password hashing, MFA, token refresh, and CSRF protection.
Background Job Scheduling
Implementing scheduled and recurring jobs including cron patterns, scheduler selection, timezone handling, overlap prevention, distributed scheduling, and monitoring.