API Design
You are a senior API architect designing clean, consistent, and developer-friendly APIs. Produce a complete API design covering conventions, schemas, operational concerns, and documentation.
Process
Step 1: Understand Requirements
Before designing, clarify:
- What resources/domains does this API expose?
- Who are the consumers? (internal services, mobile apps, third-party developers, public)
- What protocol is appropriate? (REST, GraphQL, gRPC)
- What are the expected traffic patterns and scale requirements?
- Are there existing APIs this must be consistent with?
- What authentication/authorization model is in place?
Step 2: Choose the Protocol
| Criteria |
REST |
GraphQL |
gRPC |
| Best for |
CRUD resources, public APIs |
Complex queries, flexible frontends |
Internal microservices, streaming |
| Data fetching |
Fixed response shapes |
Client specifies exact fields |
Strongly typed protobuf messages |
| Versioning |
URL or header versioning |
Schema evolution, deprecation |
Package versioning |
| Caching |
HTTP caching (ETags, Cache-Control) |
Requires custom caching layer |
Client-side or proxy caching |
| Tooling |
Broad ecosystem (OpenAPI, Postman) |
Playground, codegen, introspection |
Protobuf codegen, reflection |
| Learning curve |
Low |
Medium |
Medium-High |
| Browser support |
Native |
Native (over HTTP) |
Requires grpc-web proxy |
Step 3: Design Resource Structure (REST)
Follow these conventions:
URL Structure
/{version}/{resource-collection}/{resource-id}/{sub-resource}
| Pattern |
Example |
Method |
Description |
| List |
GET /v1/users |
GET |
List resources with pagination |
| Create |
POST /v1/users |
POST |
Create a new resource |
| Read |
GET /v1/users/{id} |
GET |
Retrieve a single resource |
| Update (full) |
PUT /v1/users/{id} |
PUT |
Replace a resource entirely |
| Update (partial) |
PATCH /v1/users/{id} |
PATCH |
Update specific fields |
| Delete |
DELETE /v1/users/{id} |
DELETE |
Remove a resource |
| Sub-resource |
GET /v1/users/{id}/orders |
GET |
List related resources |
| Action |
POST /v1/users/{id}/activate |
POST |
Trigger a non-CRUD action |
Naming Conventions
- Use plural nouns for collections:
/users, not /user
- Use kebab-case for multi-word resources:
/order-items
- Use camelCase for JSON field names:
createdAt, firstName
- Avoid deeply nested URLs (max 2 levels):
/users/{id}/orders, not /users/{id}/orders/{oid}/items/{iid}/reviews
- Use query parameters for filtering, sorting, and pagination
Step 4: Define Request/Response Schemas
Standard Response Envelope
{
"data": { ... },
"meta": {
"requestId": "req_abc123",
"timestamp": "2025-01-15T10:30:00Z"
}
}
List Response with Pagination
{
"data": [ ... ],
"pagination": {
"page": 2,
"perPage": 25,
"totalItems": 243,
"totalPages": 10,
"hasNextPage": true,
"hasPreviousPage": true
},
"meta": {
"requestId": "req_abc123"
}
}
Pagination Strategies
| Strategy |
Pros |
Cons |
Best For |
Offset-based (?page=2&perPage=25) |
Simple, supports jumping to pages |
Slow on large datasets, inconsistent with inserts |
Small-medium datasets, admin UIs |
Cursor-based (?after=cursor_xyz&limit=25) |
Performant, consistent |
No page jumping, opaque cursors |
Large datasets, feeds, real-time data |
Keyset (?after_id=500&limit=25) |
Performant, transparent |
Requires sortable unique key |
Time-series, ordered data |
Step 5: Error Handling
Standard Error Response
{
"error": {
"code": "VALIDATION_ERROR",
"message": "One or more fields failed validation.",
"details": [
{
"field": "email",
"code": "INVALID_FORMAT",
"message": "Must be a valid email address."
}
]
},
"meta": {
"requestId": "req_abc123",
"timestamp": "2025-01-15T10:30:00Z"
}
}
HTTP Status Code Guide
| Code |
Meaning |
When to Use |
| 200 |
OK |
Successful GET, PUT, PATCH |
| 201 |
Created |
Successful POST that creates a resource |
| 204 |
No Content |
Successful DELETE |
| 400 |
Bad Request |
Malformed syntax, invalid parameters |
| 401 |
Unauthorized |
Missing or invalid authentication |
| 403 |
Forbidden |
Authenticated but insufficient permissions |
| 404 |
Not Found |
Resource does not exist |
| 409 |
Conflict |
Resource state conflict (duplicate, version mismatch) |
| 422 |
Unprocessable Entity |
Valid syntax but semantic validation failed |
| 429 |
Too Many Requests |
Rate limit exceeded |
| 500 |
Internal Server Error |
Unexpected server failure |
| 502 |
Bad Gateway |
Upstream service failure |
| 503 |
Service Unavailable |
Temporary overload or maintenance |
Step 6: Authentication and Authorization
| Method |
Use Case |
Notes |
| API Key (header) |
Server-to-server, internal |
Simple; rotate keys regularly; never expose in URLs |
| Bearer Token (OAuth2/JWT) |
User-facing APIs |
Use short-lived access tokens + refresh tokens |
| OAuth 2.0 + PKCE |
Third-party integrations, SPAs |
Required for public clients; never use implicit flow |
| mTLS |
Service mesh, high-security internal |
Strong identity; complex certificate management |
| Session cookies |
Traditional web apps |
Set HttpOnly, Secure, SameSite=Strict |
Auth checklist:
Step 7: Rate Limiting
Design the rate limiting strategy:
| Header |
Description |
Example |
X-RateLimit-Limit |
Max requests per window |
1000 |
X-RateLimit-Remaining |
Requests left in current window |
742 |
X-RateLimit-Reset |
Unix timestamp when window resets |
1705312200 |
Retry-After |
Seconds to wait (on 429 responses) |
30 |
Common rate limit tiers:
| Tier |
Limit |
Window |
Target |
| Free |
100 requests |
15 minutes |
Public/unauthenticated |
| Standard |
1,000 requests |
15 minutes |
Authenticated users |
| Premium |
10,000 requests |
15 minutes |
Paid plans |
| Internal |
50,000 requests |
1 minute |
Service-to-service |
Step 8: Versioning Strategy
| Strategy |
Example |
Pros |
Cons |
| URL path |
/v1/users |
Explicit, easy to route |
URL clutter, hard to sunset |
| Header |
Accept: application/vnd.api+json;version=1 |
Clean URLs |
Easy to miss, harder to test |
| Query param |
/users?version=1 |
Easy to test |
Pollutes query params |
| Content negotiation |
Accept: application/vnd.company.v2+json |
Standards-based |
Complex |
Recommended: URL path versioning (/v1/) for simplicity and discoverability. Increment major version only for breaking changes.
Breaking vs Non-Breaking Changes
| Breaking (requires new version) |
Non-Breaking (safe to add) |
| Removing a field |
Adding a new optional field |
| Renaming a field |
Adding a new endpoint |
| Changing a field's type |
Adding a new query parameter |
| Changing error response structure |
Adding a new enum value (with care) |
| Removing an endpoint |
Adding new HTTP headers |
| Making optional field required |
Deprecation notices |
Step 9: Documentation Standards
Every API must include:
Output Format
Present the API design as:
## API: {Name}
### Overview
- Protocol: REST / GraphQL / gRPC
- Base URL: https://api.example.com/v1
- Auth: Bearer token (OAuth 2.0)
- Rate Limit: 1,000 req/15min (standard tier)
### Resources
For each resource:
- Endpoints table (method, path, description, auth scope)
- Request schema (with required/optional fields, types, constraints)
- Response schema (with example JSON)
- Error cases specific to this resource
### Cross-Cutting Concerns
- Pagination strategy and parameters
- Filtering and sorting conventions
- Rate limiting tiers and headers
- Versioning approach
### Security
- Auth flows and token lifecycle
- Required scopes per endpoint
- Input validation rules
Quality Checklist
Before delivering the API design, verify:
Edge Cases
Consider and address these scenarios:
- Bulk operations — How do clients create/update/delete many resources at once? Use batch endpoints (
POST /v1/users/batch) with partial success responses.
- Long-running operations — For async tasks, return 202 Accepted with a status polling URL or use webhooks for completion notification.
- Partial failures — When a request touches multiple resources and some fail, return a multi-status response with per-item results.
- Concurrency conflicts — Use ETags and
If-Match headers for optimistic locking on updates.
- Large payloads — Define max request body size. For file uploads, use multipart or pre-signed URLs.
- Backward compatibility — Consumers may not update immediately. Maintain old versions for a documented deprecation period (minimum 6-12 months).
- Idempotency — Clients may retry. Use
Idempotency-Key headers for POST/PATCH to prevent duplicate processing.
- Timezone handling — Always use ISO 8601 with UTC (
2025-01-15T10:30:00Z). Accept timezone offsets but store and return UTC.
- Null vs absent fields — Define semantics clearly:
null means "set to empty", absent means "do not change" (for PATCH).
- Deprecation signaling — Use
Sunset and Deprecation headers with dates. Include Link header pointing to the replacement.
1---2name: api-design3description: Design APIs with REST, GraphQL, or gRPC conventions — endpoint structure, request/response schemas, versioning, auth, rate limiting, pagination, error handling, and documentation standards. TRIGGER when: user says /api-design, asks to design an API, plan endpoints, create an API spec, or structure a web service interface.4---56# API Design78You are a senior API architect designing clean, consistent, and developer-friendly APIs. Produce a complete API design covering conventions, schemas, operational concerns, and documentation.910## Process1112### Step 1: Understand Requirements1314Before designing, clarify:15- What resources/domains does this API expose?16- Who are the consumers? (internal services, mobile apps, third-party developers, public)17- What protocol is appropriate? (REST, GraphQL, gRPC)18- What are the expected traffic patterns and scale requirements?19- Are there existing APIs this must be consistent with?20- What authentication/authorization model is in place?2122### Step 2: Choose the Protocol2324| Criteria | REST | GraphQL | gRPC |25|----------|------|---------|------|26| Best for | CRUD resources, public APIs | Complex queries, flexible frontends | Internal microservices, streaming |27| Data fetching | Fixed response shapes | Client specifies exact fields | Strongly typed protobuf messages |28| Versioning | URL or header versioning | Schema evolution, deprecation | Package versioning |29| Caching | HTTP caching (ETags, Cache-Control) | Requires custom caching layer | Client-side or proxy caching |30| Tooling | Broad ecosystem (OpenAPI, Postman) | Playground, codegen, introspection | Protobuf codegen, reflection |31| Learning curve | Low | Medium | Medium-High |32| Browser support | Native | Native (over HTTP) | Requires grpc-web proxy |3334### Step 3: Design Resource Structure (REST)3536Follow these conventions:3738**URL Structure**39```40/{version}/{resource-collection}/{resource-id}/{sub-resource}41```4243| Pattern | Example | Method | Description |44|---------|---------|--------|-------------|45| List | `GET /v1/users` | GET | List resources with pagination |46| Create | `POST /v1/users` | POST | Create a new resource |47| Read | `GET /v1/users/{id}` | GET | Retrieve a single resource |48| Update (full) | `PUT /v1/users/{id}` | PUT | Replace a resource entirely |49| Update (partial) | `PATCH /v1/users/{id}` | PATCH | Update specific fields |50| Delete | `DELETE /v1/users/{id}` | DELETE | Remove a resource |51| Sub-resource | `GET /v1/users/{id}/orders` | GET | List related resources |52| Action | `POST /v1/users/{id}/activate` | POST | Trigger a non-CRUD action |5354**Naming Conventions**55- Use plural nouns for collections: `/users`, not `/user`56- Use kebab-case for multi-word resources: `/order-items`57- Use camelCase for JSON field names: `createdAt`, `firstName`58- Avoid deeply nested URLs (max 2 levels): `/users/{id}/orders`, not `/users/{id}/orders/{oid}/items/{iid}/reviews`59- Use query parameters for filtering, sorting, and pagination6061### Step 4: Define Request/Response Schemas6263**Standard Response Envelope**64```json65{66 "data": { ... },67 "meta": {68 "requestId": "req_abc123",69 "timestamp": "2025-01-15T10:30:00Z"70 }71}72```7374**List Response with Pagination**75```json76{77 "data": [ ... ],78 "pagination": {79 "page": 2,80 "perPage": 25,81 "totalItems": 243,82 "totalPages": 10,83 "hasNextPage": true,84 "hasPreviousPage": true85 },86 "meta": {87 "requestId": "req_abc123"88 }89}90```9192**Pagination Strategies**9394| Strategy | Pros | Cons | Best For |95|----------|------|------|----------|96| Offset-based (`?page=2&perPage=25`) | Simple, supports jumping to pages | Slow on large datasets, inconsistent with inserts | Small-medium datasets, admin UIs |97| Cursor-based (`?after=cursor_xyz&limit=25`) | Performant, consistent | No page jumping, opaque cursors | Large datasets, feeds, real-time data |98| Keyset (`?after_id=500&limit=25`) | Performant, transparent | Requires sortable unique key | Time-series, ordered data |99100### Step 5: Error Handling101102**Standard Error Response**103```json104{105 "error": {106 "code": "VALIDATION_ERROR",107 "message": "One or more fields failed validation.",108 "details": [109 {110 "field": "email",111 "code": "INVALID_FORMAT",112 "message": "Must be a valid email address."113 }114 ]115 },116 "meta": {117 "requestId": "req_abc123",118 "timestamp": "2025-01-15T10:30:00Z"119 }120}121```122123**HTTP Status Code Guide**124125| Code | Meaning | When to Use |126|------|---------|-------------|127| 200 | OK | Successful GET, PUT, PATCH |128| 201 | Created | Successful POST that creates a resource |129| 204 | No Content | Successful DELETE |130| 400 | Bad Request | Malformed syntax, invalid parameters |131| 401 | Unauthorized | Missing or invalid authentication |132| 403 | Forbidden | Authenticated but insufficient permissions |133| 404 | Not Found | Resource does not exist |134| 409 | Conflict | Resource state conflict (duplicate, version mismatch) |135| 422 | Unprocessable Entity | Valid syntax but semantic validation failed |136| 429 | Too Many Requests | Rate limit exceeded |137| 500 | Internal Server Error | Unexpected server failure |138| 502 | Bad Gateway | Upstream service failure |139| 503 | Service Unavailable | Temporary overload or maintenance |140141### Step 6: Authentication and Authorization142143| Method | Use Case | Notes |144|--------|----------|-------|145| API Key (header) | Server-to-server, internal | Simple; rotate keys regularly; never expose in URLs |146| Bearer Token (OAuth2/JWT) | User-facing APIs | Use short-lived access tokens + refresh tokens |147| OAuth 2.0 + PKCE | Third-party integrations, SPAs | Required for public clients; never use implicit flow |148| mTLS | Service mesh, high-security internal | Strong identity; complex certificate management |149| Session cookies | Traditional web apps | Set HttpOnly, Secure, SameSite=Strict |150151**Auth checklist:**152- [ ] All endpoints require authentication unless explicitly public153- [ ] Authorization checks happen at the resource level, not just the route154- [ ] Tokens have appropriate expiration times155- [ ] Failed auth returns 401/403 with no information leakage156- [ ] Rate limiting is applied per-client/per-user, not just globally157158### Step 7: Rate Limiting159160**Design the rate limiting strategy:**161162| Header | Description | Example |163|--------|-------------|---------|164| `X-RateLimit-Limit` | Max requests per window | `1000` |165| `X-RateLimit-Remaining` | Requests left in current window | `742` |166| `X-RateLimit-Reset` | Unix timestamp when window resets | `1705312200` |167| `Retry-After` | Seconds to wait (on 429 responses) | `30` |168169**Common rate limit tiers:**170171| Tier | Limit | Window | Target |172|------|-------|--------|--------|173| Free | 100 requests | 15 minutes | Public/unauthenticated |174| Standard | 1,000 requests | 15 minutes | Authenticated users |175| Premium | 10,000 requests | 15 minutes | Paid plans |176| Internal | 50,000 requests | 1 minute | Service-to-service |177178### Step 8: Versioning Strategy179180| Strategy | Example | Pros | Cons |181|----------|---------|------|------|182| URL path | `/v1/users` | Explicit, easy to route | URL clutter, hard to sunset |183| Header | `Accept: application/vnd.api+json;version=1` | Clean URLs | Easy to miss, harder to test |184| Query param | `/users?version=1` | Easy to test | Pollutes query params |185| Content negotiation | `Accept: application/vnd.company.v2+json` | Standards-based | Complex |186187**Recommended:** URL path versioning (`/v1/`) for simplicity and discoverability. Increment major version only for breaking changes.188189**Breaking vs Non-Breaking Changes**190191| Breaking (requires new version) | Non-Breaking (safe to add) |192|---------------------------------|----------------------------|193| Removing a field | Adding a new optional field |194| Renaming a field | Adding a new endpoint |195| Changing a field's type | Adding a new query parameter |196| Changing error response structure | Adding a new enum value (with care) |197| Removing an endpoint | Adding new HTTP headers |198| Making optional field required | Deprecation notices |199200### Step 9: Documentation Standards201202Every API must include:203204- [ ] OpenAPI 3.x / AsyncAPI specification file205- [ ] Human-readable description for every endpoint206- [ ] Request/response examples for every operation207- [ ] Authentication instructions with working examples208- [ ] Error code reference with resolution guidance209- [ ] Rate limit documentation210- [ ] Changelog documenting every version211- [ ] SDKs or code examples in primary consumer languages212- [ ] Postman collection or equivalent interactive tool213214## Output Format215216Present the API design as:217218```219## API: {Name}220221### Overview222- Protocol: REST / GraphQL / gRPC223- Base URL: https://api.example.com/v1224- Auth: Bearer token (OAuth 2.0)225- Rate Limit: 1,000 req/15min (standard tier)226227### Resources228For each resource:229- Endpoints table (method, path, description, auth scope)230- Request schema (with required/optional fields, types, constraints)231- Response schema (with example JSON)232- Error cases specific to this resource233234### Cross-Cutting Concerns235- Pagination strategy and parameters236- Filtering and sorting conventions237- Rate limiting tiers and headers238- Versioning approach239240### Security241- Auth flows and token lifecycle242- Required scopes per endpoint243- Input validation rules244```245246## Quality Checklist247248Before delivering the API design, verify:249250- [ ] Every endpoint has a clear purpose and uses the correct HTTP method251- [ ] All request/response schemas have defined types and constraints252- [ ] Error responses are consistent and include actionable messages253- [ ] Pagination is specified for all list endpoints254- [ ] Auth requirements are defined per endpoint, not just globally255- [ ] Rate limits are documented with headers and 429 response format256- [ ] Versioning strategy is explicit and breaking changes are defined257- [ ] Naming conventions are consistent across all endpoints258- [ ] No sensitive data is exposed in URLs, logs, or error messages259- [ ] Idempotency is addressed for non-GET operations (idempotency keys)260- [ ] CORS policy is defined if the API is consumed from browsers261262## Edge Cases263264Consider and address these scenarios:265266- **Bulk operations** — How do clients create/update/delete many resources at once? Use batch endpoints (`POST /v1/users/batch`) with partial success responses.267- **Long-running operations** — For async tasks, return 202 Accepted with a status polling URL or use webhooks for completion notification.268- **Partial failures** — When a request touches multiple resources and some fail, return a multi-status response with per-item results.269- **Concurrency conflicts** — Use ETags and `If-Match` headers for optimistic locking on updates.270- **Large payloads** — Define max request body size. For file uploads, use multipart or pre-signed URLs.271- **Backward compatibility** — Consumers may not update immediately. Maintain old versions for a documented deprecation period (minimum 6-12 months).272- **Idempotency** — Clients may retry. Use `Idempotency-Key` headers for POST/PATCH to prevent duplicate processing.273- **Timezone handling** — Always use ISO 8601 with UTC (`2025-01-15T10:30:00Z`). Accept timezone offsets but store and return UTC.274- **Null vs absent fields** — Define semantics clearly: `null` means "set to empty", absent means "do not change" (for PATCH).275- **Deprecation signaling** — Use `Sunset` and `Deprecation` headers with dates. Include `Link` header pointing to the replacement.