API Patterns Reference
Target Spawn
Backend domain work spawned via Agent(general-purpose) with backend instructions - Applies these patterns directly to API implementation and review.
RESTful API Design Conventions
| Principle |
Convention |
Example |
| Resource Naming |
Plural nouns, lowercase, kebab-case |
/api/v1/user-profiles |
| Collection |
GET returns array with pagination |
GET /users?page=1&limit=20 |
| Single Resource |
GET returns object |
GET /users/{id} |
| Create |
POST to collection |
POST /users |
| Update (full) |
PUT to resource |
PUT /users/{id} |
| Update (partial) |
PATCH to resource |
PATCH /users/{id} |
| Delete |
DELETE to resource |
DELETE /users/{id} |
| Nested Resources |
Max 2 levels deep |
/users/{id}/posts |
| Filtering |
Query params |
?status=active&role=admin |
| Sorting |
Sort param |
?sort=-created_at,name |
| Versioning |
URL prefix |
/api/v1/, /api/v2/ |
HTTP Status Code Guide
| Category |
Code |
When to Use |
| Success |
200 OK |
Successful GET, PUT, PATCH, DELETE |
| Success |
201 Created |
Successful POST (resource created) |
| Success |
204 No Content |
Successful DELETE (no body) |
| Client Error |
400 Bad Request |
Malformed request, validation failure |
| Client Error |
401 Unauthorized |
Missing or invalid authentication |
| Client Error |
403 Forbidden |
Authenticated but not authorized |
| Client Error |
404 Not Found |
Resource does not exist |
| Client Error |
409 Conflict |
Resource state conflict (duplicate) |
| Client Error |
422 Unprocessable |
Valid syntax but semantic error |
| Client Error |
429 Too Many |
Rate limit exceeded |
| Server Error |
500 Internal |
Unexpected server error |
| Server Error |
503 Service Unavailable |
Maintenance or overload |
Error Response Format
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Input validation failed",
"details": [
{"field": "email", "message": "Must be a valid email address"},
{"field": "age", "message": "Must be between 0 and 150"}
],
"request_id": "req_abc123"
}
}
Rules:
- Never expose stack traces or internal details in production
- Always include request_id for traceability
- Use consistent error codes (ENUM, not free text)
- Login failures: "Invalid email or password" (never reveal which)
Pagination Pattern
{
"data": [...],
"pagination": {
"page": 1,
"limit": 20,
"total": 150,
"total_pages": 8,
"has_next": true,
"has_prev": false
}
}
For cursor-based (large datasets):
{
"data": [...],
"cursor": {
"next": "eyJpZCI6MTAwfQ==",
"has_more": true
}
}
Input Validation Checklist
| Validation |
Method |
Tool |
| Type validation |
Schema validation |
Zod, Joi, pydantic, Go validator |
| Length limits |
Min/max constraints |
Schema min/max |
| Pattern matching |
Regex |
Email, URL, phone patterns |
| Range validation |
Number/date bounds |
min/max values |
| Enumeration |
Allowed values |
enum types |
| SQL Injection |
Parameterized queries |
ORM (Prisma, GORM, SQLAlchemy) |
| XSS |
HTML escaping |
Template engines, DOMPurify |
| Path Traversal |
Path normalization |
filepath.Clean + whitelist |
Rate Limiting Strategy
| Target |
Limit |
Key |
| Auth endpoints |
5 req/min |
IP |
| General API |
100 req/min |
User token |
| File upload |
10 req/hour |
User token |
| Public API |
30 req/min |
IP |
Response headers: X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, Retry-After (on 429).
API Versioning Strategy
| Strategy |
Use Case |
Example |
| URL prefix |
Most APIs |
/api/v1/users |
| Header |
Internal APIs |
Accept: application/vnd.api+json; version=2 |
| Query param |
Simple APIs |
/users?version=2 |
Breaking changes that require version bump:
- Removing or renaming fields
- Changing field types
- Removing endpoints
- Changing authentication methods
Non-breaking changes (no version bump needed):
- Adding new optional fields
- Adding new endpoints
- Adding new query parameters
Common Rationalizations
| Rationalization |
Reality |
| "REST naming conventions are just aesthetics" |
Consistent resource naming is how clients discover and predict endpoints. Inconsistency multiplies documentation burden. |
| "GraphQL solves over-fetching, so I do not need to design response shapes" |
GraphQL shifts complexity to the resolver layer. Poorly designed schemas create N+1 queries and authorization gaps. |
| "Error codes are internal details, clients just need the message" |
Clients need machine-readable error codes for programmatic handling. Messages are for humans, codes are for code. |
| "PATCH and PUT are interchangeable" |
PATCH applies partial updates; PUT replaces the entire resource. Using them incorrectly breaks idempotency expectations. |
| "I will version the API when it becomes necessary" |
Versioning after breaking changes forces emergency migrations. Plan versioning from the first release. |
Hyrum's Law: Every observable API behavior will eventually be depended on by clients. Undocumented response fields, error formats, and timing characteristics become implicit contracts.
Red Flags
- API returns different error formats across endpoints
- Resource names use verbs instead of nouns (e.g., /getUser instead of /users/:id)
- No pagination on list endpoints that can return unbounded results
- Breaking change deployed without API version bump
- GraphQL schema allows unbounded depth or circular queries without limits
Verification
1---2name: moai-ref-api-patterns3description: REST/GraphQL API design patterns, error handling conventions, and input validation reference for backend development. Agent-extending skill that amplifies backend domain work (spawned via Agent(general-purpose) with backend instructions) with production-grade API patterns. Use when designing APIs, implementing endpoints, or reviewing backend code. NOT for: frontend development, DevOps, database schema design, security audits.4---5
6# API Patterns Reference
7
8## Target Spawn
9
10Backend domain work spawned via `Agent(general-purpose)` with backend instructions - Applies these patterns directly to API implementation and review.
11
12## RESTful API Design Conventions
13
14| Principle | Convention | Example |
15|-----------|-----------|---------|
16| Resource Naming | Plural nouns, lowercase, kebab-case | `/api/v1/user-profiles` |
17| Collection | GET returns array with pagination | `GET /users?page=1&limit=20` |
18| Single Resource | GET returns object | `GET /users/{id}` |
19| Create | POST to collection | `POST /users` |
20| Update (full) | PUT to resource | `PUT /users/{id}` |
21| Update (partial) | PATCH to resource | `PATCH /users/{id}` |
22| Delete | DELETE to resource | `DELETE /users/{id}` |
23| Nested Resources | Max 2 levels deep | `/users/{id}/posts` |
24| Filtering | Query params | `?status=active&role=admin` |
25| Sorting | Sort param | `?sort=-created_at,name` |
26| Versioning | URL prefix | `/api/v1/`, `/api/v2/` |
27
28## HTTP Status Code Guide
29
30| Category | Code | When to Use |
31|----------|------|-------------|
32| Success | 200 OK | Successful GET, PUT, PATCH, DELETE |
33| Success | 201 Created | Successful POST (resource created) |
34| Success | 204 No Content | Successful DELETE (no body) |
35| Client Error | 400 Bad Request | Malformed request, validation failure |
36| Client Error | 401 Unauthorized | Missing or invalid authentication |
37| Client Error | 403 Forbidden | Authenticated but not authorized |
38| Client Error | 404 Not Found | Resource does not exist |
39| Client Error | 409 Conflict | Resource state conflict (duplicate) |
40| Client Error | 422 Unprocessable | Valid syntax but semantic error |
41| Client Error | 429 Too Many | Rate limit exceeded |
42| Server Error | 500 Internal | Unexpected server error |
43| Server Error | 503 Service Unavailable | Maintenance or overload |
44
45## Error Response Format
46
47```json
48{
49 "error": {
50 "code": "VALIDATION_ERROR",
51 "message": "Input validation failed",
52 "details": [
53 {"field": "email", "message": "Must be a valid email address"},
54 {"field": "age", "message": "Must be between 0 and 150"}
55 ],
56 "request_id": "req_abc123"
57 }
58}
59```
60
61Rules:
62- Never expose stack traces or internal details in production
63- Always include request_id for traceability
64- Use consistent error codes (ENUM, not free text)
65- Login failures: "Invalid email or password" (never reveal which)
66
67## Pagination Pattern
68
69```json
70{
71 "data": [...],
72 "pagination": {
73 "page": 1,
74 "limit": 20,
75 "total": 150,
76 "total_pages": 8,
77 "has_next": true,
78 "has_prev": false
79 }
80}
81```
82
83For cursor-based (large datasets):
84```json
85{
86 "data": [...],
87 "cursor": {
88 "next": "eyJpZCI6MTAwfQ==",
89 "has_more": true
90 }
91}
92```
93
94## Input Validation Checklist
95
96| Validation | Method | Tool |
97|-----------|--------|------|
98| Type validation | Schema validation | Zod, Joi, pydantic, Go validator |
99| Length limits | Min/max constraints | Schema min/max |
100| Pattern matching | Regex | Email, URL, phone patterns |
101| Range validation | Number/date bounds | min/max values |
102| Enumeration | Allowed values | enum types |
103| SQL Injection | Parameterized queries | ORM (Prisma, GORM, SQLAlchemy) |
104| XSS | HTML escaping | Template engines, DOMPurify |
105| Path Traversal | Path normalization | filepath.Clean + whitelist |
106
107## Rate Limiting Strategy
108
109| Target | Limit | Key |
110|--------|-------|-----|
111| Auth endpoints | 5 req/min | IP |
112| General API | 100 req/min | User token |
113| File upload | 10 req/hour | User token |
114| Public API | 30 req/min | IP |
115
116Response headers: `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`, `Retry-After` (on 429).
117
118## API Versioning Strategy
119
120| Strategy | Use Case | Example |
121|----------|----------|---------|
122| URL prefix | Most APIs | `/api/v1/users` |
123| Header | Internal APIs | `Accept: application/vnd.api+json; version=2` |
124| Query param | Simple APIs | `/users?version=2` |
125
126Breaking changes that require version bump:
127- Removing or renaming fields
128- Changing field types
129- Removing endpoints
130- Changing authentication methods
131
132Non-breaking changes (no version bump needed):
133- Adding new optional fields
134- Adding new endpoints
135- Adding new query parameters
136
137<!-- moai:evolvable-start id="rationalizations" -->
138## Common Rationalizations
139
140| Rationalization | Reality |
141|---|---|
142| "REST naming conventions are just aesthetics" | Consistent resource naming is how clients discover and predict endpoints. Inconsistency multiplies documentation burden. |
143| "GraphQL solves over-fetching, so I do not need to design response shapes" | GraphQL shifts complexity to the resolver layer. Poorly designed schemas create N+1 queries and authorization gaps. |
144| "Error codes are internal details, clients just need the message" | Clients need machine-readable error codes for programmatic handling. Messages are for humans, codes are for code. |
145| "PATCH and PUT are interchangeable" | PATCH applies partial updates; PUT replaces the entire resource. Using them incorrectly breaks idempotency expectations. |
146| "I will version the API when it becomes necessary" | Versioning after breaking changes forces emergency migrations. Plan versioning from the first release. |
147
148**Hyrum's Law**: Every observable API behavior will eventually be depended on by clients. Undocumented response fields, error formats, and timing characteristics become implicit contracts.
149
150<!-- moai:evolvable-end -->
151
152<!-- moai:evolvable-start id="red-flags" -->
153## Red Flags
154
155- API returns different error formats across endpoints
156- Resource names use verbs instead of nouns (e.g., /getUser instead of /users/:id)
157- No pagination on list endpoints that can return unbounded results
158- Breaking change deployed without API version bump
159- GraphQL schema allows unbounded depth or circular queries without limits
160
161<!-- moai:evolvable-end -->
162
163<!-- moai:evolvable-start id="verification" -->
164## Verification
165
166- [ ] All endpoints follow consistent naming convention (nouns, plurals, nested resources)
167- [ ] Error responses use a standard format with machine-readable error code
168- [ ] List endpoints implement pagination with documented limits
169- [ ] API versioning strategy present and enforced (URL path, header, or query param)
170- [ ] Breaking vs non-breaking change classification documented for recent changes
171- [ ] Input validation returns 400 with specific field-level error details
172
173<!-- moai:evolvable-end -->