$npx -y skills add affaan-m/ECC --skill api-designREST API design patterns including resource naming, status codes, pagination, filtering, error responses, versioning, and rate limiting for production APIs.
| 1 | # API Design Patterns |
| 2 | |
| 3 | Conventions and best practices for designing consistent, developer-friendly REST APIs. |
| 4 | |
| 5 | ## When to Activate |
| 6 | |
| 7 | - Designing new API endpoints |
| 8 | - Reviewing existing API contracts |
| 9 | - Adding pagination, filtering, or sorting |
| 10 | - Implementing error handling for APIs |
| 11 | - Planning API versioning strategy |
| 12 | - Building public or partner-facing APIs |
| 13 | |
| 14 | ## Resource Design |
| 15 | |
| 16 | ### URL Structure |
| 17 | |
| 18 | ``` |
| 19 | # Resources are nouns, plural, lowercase, kebab-case |
| 20 | GET /api/v1/users |
| 21 | GET /api/v1/users/:id |
| 22 | POST /api/v1/users |
| 23 | PUT /api/v1/users/:id |
| 24 | PATCH /api/v1/users/:id |
| 25 | DELETE /api/v1/users/:id |
| 26 | |
| 27 | # Sub-resources for relationships |
| 28 | GET /api/v1/users/:id/orders |
| 29 | POST /api/v1/users/:id/orders |
| 30 | |
| 31 | # Actions that don't map to CRUD (use verbs sparingly) |
| 32 | POST /api/v1/orders/:id/cancel |
| 33 | POST /api/v1/auth/login |
| 34 | POST /api/v1/auth/refresh |
| 35 | ``` |
| 36 | |
| 37 | ### Naming Rules |
| 38 | |
| 39 | ``` |
| 40 | # GOOD |
| 41 | /api/v1/team-members # kebab-case for multi-word resources |
| 42 | /api/v1/orders?status=active # query params for filtering |
| 43 | /api/v1/users/123/orders # nested resources for ownership |
| 44 | |
| 45 | # BAD |
| 46 | /api/v1/getUsers # verb in URL |
| 47 | /api/v1/user # singular (use plural) |
| 48 | /api/v1/team_members # snake_case in URLs |
| 49 | /api/v1/users/123/getOrders # verb in nested resource |
| 50 | ``` |
| 51 | |
| 52 | ## HTTP Methods and Status Codes |
| 53 | |
| 54 | ### Method Semantics |
| 55 | |
| 56 | | Method | Idempotent | Safe | Use For | |
| 57 | |--------|-----------|------|---------| |
| 58 | | GET | Yes | Yes | Retrieve resources | |
| 59 | | POST | No | No | Create resources, trigger actions | |
| 60 | | PUT | Yes | No | Full replacement of a resource | |
| 61 | | PATCH | No* | No | Partial update of a resource | |
| 62 | | DELETE | Yes | No | Remove a resource | |
| 63 | |
| 64 | *PATCH can be made idempotent with proper implementation |
| 65 | |
| 66 | ### Status Code Reference |
| 67 | |
| 68 | ``` |
| 69 | # Success |
| 70 | 200 OK — GET, PUT, PATCH (with response body) |
| 71 | 201 Created — POST (include Location header) |
| 72 | 204 No Content — DELETE, PUT (no response body) |
| 73 | |
| 74 | # Client Errors |
| 75 | 400 Bad Request — Validation failure, malformed JSON |
| 76 | 401 Unauthorized — Missing or invalid authentication |
| 77 | 403 Forbidden — Authenticated but not authorized |
| 78 | 404 Not Found — Resource doesn't exist |
| 79 | 409 Conflict — Duplicate entry, state conflict |
| 80 | 422 Unprocessable Entity — Semantically invalid (valid JSON, bad data) |
| 81 | 429 Too Many Requests — Rate limit exceeded |
| 82 | |
| 83 | # Server Errors |
| 84 | 500 Internal Server Error — Unexpected failure (never expose details) |
| 85 | 502 Bad Gateway — Upstream service failed |
| 86 | 503 Service Unavailable — Temporary overload, include Retry-After |
| 87 | ``` |
| 88 | |
| 89 | ### Common Mistakes |
| 90 | |
| 91 | ``` |
| 92 | # BAD: 200 for everything |
| 93 | { "status": 200, "success": false, "error": "Not found" } |
| 94 | |
| 95 | # GOOD: Use HTTP status codes semantically |
| 96 | HTTP/1.1 404 Not Found |
| 97 | { "error": { "code": "not_found", "message": "User not found" } } |
| 98 | |
| 99 | # BAD: 500 for validation errors |
| 100 | # GOOD: 400 or 422 with field-level details |
| 101 | |
| 102 | # BAD: 200 for created resources |
| 103 | # GOOD: 201 with Location header |
| 104 | HTTP/1.1 201 Created |
| 105 | Location: /api/v1/users/abc-123 |
| 106 | ``` |
| 107 | |
| 108 | ## Response Format |
| 109 | |
| 110 | ### Success Response |
| 111 | |
| 112 | ```json |
| 113 | { |
| 114 | "data": { |
| 115 | "id": "abc-123", |
| 116 | "email": "alice@example.com", |
| 117 | "name": "Alice", |
| 118 | "created_at": "2025-01-15T10:30:00Z" |
| 119 | } |
| 120 | } |
| 121 | ``` |
| 122 | |
| 123 | ### Collection Response (with Pagination) |
| 124 | |
| 125 | ```json |
| 126 | { |
| 127 | "data": [ |
| 128 | { "id": "abc-123", "name": "Alice" }, |
| 129 | { "id": "def-456", "name": "Bob" } |
| 130 | ], |
| 131 | "meta": { |
| 132 | "total": 142, |
| 133 | "page": 1, |
| 134 | "per_page": 20, |
| 135 | "total_pages": 8 |
| 136 | }, |
| 137 | "links": { |
| 138 | "self": "/api/v1/users?page=1&per_page=20", |
| 139 | "next": "/api/v1/users?page=2&per_page=20", |
| 140 | "last": "/api/v1/users?page=8&per_page=20" |
| 141 | } |
| 142 | } |
| 143 | ``` |
| 144 | |
| 145 | ### Error Response |
| 146 | |
| 147 | ```json |
| 148 | { |
| 149 | "error": { |
| 150 | "code": "validation_error", |
| 151 | "message": "Request validation failed", |
| 152 | "details": [ |
| 153 | { |
| 154 | "field": "email", |
| 155 | "message": "Must be a valid email address", |
| 156 | "code": "invalid_format" |
| 157 | }, |
| 158 | { |
| 159 | "field": "age", |
| 160 | "message": "Must be between 0 and 150", |
| 161 | "code": "out_of_range" |
| 162 | } |
| 163 | ] |
| 164 | } |
| 165 | } |
| 166 | ``` |
| 167 | |
| 168 | ### Response Envelope Variants |
| 169 | |
| 170 | ```typescript |
| 171 | // Option A: Envelope with data wrapper (recommended for public APIs) |
| 172 | interface ApiResponse<T> { |
| 173 | data: T; |
| 174 | meta?: PaginationMeta; |
| 175 | links?: PaginationLinks; |
| 176 | } |
| 177 | |
| 178 | interface ApiError { |
| 179 | error: { |
| 180 | code: string; |
| 181 | message: string; |
| 182 | details?: FieldError[]; |
| 183 | }; |
| 184 | } |
| 185 | |
| 186 | // Option B: Flat response (simpler, common for internal APIs) |
| 187 | // Success: just return the resource directly |
| 188 | // Error: return error object |
| 189 | // Distinguish by HTTP status code |
| 190 | ``` |
| 191 | |
| 192 | ## Pagination |
| 193 | |
| 194 | ### Offset-Based (Simple) |
| 195 | |
| 196 | ``` |
| 197 | GET /api/v1/users?page=2&per_page=20 |
| 198 | |
| 199 | # Implementation |
| 200 | SELECT * FROM users |
| 201 | ORDER BY created_at DESC |
| 202 | LIMIT 20 OFFSET 20; |
| 203 | ``` |
| 204 | |
| 205 | **Pros:** Easy to implement, supports "jump to page N" |
| 206 | **Cons:** Slow on large offsets (OFFSET 100000), inc |