@torus-engineering/tas-kit 1.13.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. package/.tas/_platform/claude-code/settings.json +58 -46
  2. package/.tas/_platform/hooks/code-quality.js +127 -127
  3. package/.tas/_platform/hooks/session-end.js +111 -111
  4. package/.tas/agents/architect.md +53 -53
  5. package/.tas/agents/aws-reviewer.md +71 -71
  6. package/.tas/agents/build-resolver.md +89 -59
  7. package/.tas/agents/code-explorer.md +63 -63
  8. package/.tas/agents/csharp-reviewer.md +62 -62
  9. package/.tas/agents/database-reviewer.md +73 -73
  10. package/.tas/agents/doc-updater.md +68 -66
  11. package/.tas/agents/python-reviewer.md +67 -67
  12. package/.tas/agents/security-reviewer.md +79 -79
  13. package/.tas/agents/software-engineer.md +53 -0
  14. package/.tas/agents/typescript-reviewer.md +65 -65
  15. package/.tas/commands/ado-create.md +33 -28
  16. package/.tas/commands/ado-delete.md +26 -22
  17. package/.tas/commands/ado-get.md +24 -20
  18. package/.tas/commands/ado-status.md +22 -18
  19. package/.tas/commands/ado-update.md +31 -27
  20. package/.tas/commands/tas-adr.md +37 -33
  21. package/.tas/commands/tas-apitest-plan.md +177 -173
  22. package/.tas/commands/tas-apitest.md +147 -143
  23. package/.tas/commands/tas-brainstorm.md +23 -19
  24. package/.tas/commands/tas-brd.md +50 -0
  25. package/.tas/commands/tas-bug.md +127 -113
  26. package/.tas/commands/tas-checklist.md +180 -0
  27. package/.tas/commands/tas-debug.md +103 -0
  28. package/.tas/commands/tas-design.md +41 -37
  29. package/.tas/commands/tas-dev.md +225 -125
  30. package/.tas/commands/tas-e2e-mobile.md +146 -155
  31. package/.tas/commands/tas-e2e-web.md +150 -163
  32. package/.tas/commands/tas-e2e.md +289 -102
  33. package/.tas/commands/tas-feature.md +181 -47
  34. package/.tas/commands/tas-fix.md +72 -51
  35. package/.tas/commands/tas-functest-mobile.md +138 -144
  36. package/.tas/commands/tas-functest-web.md +176 -192
  37. package/.tas/commands/tas-functest.md +225 -76
  38. package/.tas/commands/tas-init.md +22 -17
  39. package/.tas/commands/tas-master-plan.md +300 -0
  40. package/.tas/commands/tas-orchestrate.md +159 -0
  41. package/.tas/commands/tas-plan.md +152 -117
  42. package/.tas/commands/tas-prd.md +57 -37
  43. package/.tas/commands/tas-review-pr.md +174 -0
  44. package/.tas/commands/tas-review.md +115 -113
  45. package/.tas/commands/tas-sad.md +47 -43
  46. package/.tas/commands/tas-security.md +91 -87
  47. package/.tas/commands/tas-spec.md +54 -50
  48. package/.tas/commands/tas-status.md +25 -16
  49. package/.tas/project-status-example.yaml +3 -1
  50. package/.tas/rules/ado-integration.md +67 -65
  51. package/.tas/rules/common/api-design.md +517 -517
  52. package/.tas/rules/common/build-debug-loop.md +233 -0
  53. package/.tas/rules/common/code-review.md +4 -0
  54. package/.tas/rules/common/feature-done.md +42 -0
  55. package/.tas/rules/common/post-implementation-review.md +4 -0
  56. package/.tas/rules/common/project-status.md +33 -16
  57. package/.tas/rules/common/sad-impact.md +81 -0
  58. package/.tas/rules/common/tdd.md +104 -89
  59. package/.tas/rules/csharp/api-testing.md +2 -2
  60. package/.tas/rules/csharp/torus-core-framework.md +128 -0
  61. package/.tas/tas-example.yaml +9 -32
  62. package/.tas/templates/AGENTS.md +13 -0
  63. package/.tas/templates/API-Test-Spec.md +5 -4
  64. package/.tas/templates/BRD.md +133 -0
  65. package/.tas/templates/Bug.md +15 -0
  66. package/.tas/templates/E2E-Execution-Report.md +8 -8
  67. package/.tas/templates/E2E-Mobile-Spec.md +6 -8
  68. package/.tas/templates/E2E-Report.md +2 -2
  69. package/.tas/templates/E2E-Scenario.md +22 -22
  70. package/.tas/templates/E2E-Test-Spec.md +274 -0
  71. package/.tas/templates/E2E-Web-Spec.md +4 -4
  72. package/.tas/templates/Feature-Technical-Part.md +69 -0
  73. package/.tas/templates/Feature-Technical-Stack.md +74 -0
  74. package/.tas/templates/Feature-Technical.md +329 -0
  75. package/.tas/templates/Feature.md +50 -26
  76. package/.tas/templates/Func-Test-Script.md +29 -56
  77. package/.tas/templates/Func-Test-Spec.md +144 -142
  78. package/.tas/templates/PRD.md +173 -142
  79. package/.tas/templates/TestChecklist.md +96 -0
  80. package/.tas/templates/torus-dotnet-bootstrap.md +223 -0
  81. package/.tas/tools/tas-ado-readme.md +24 -27
  82. package/.tas/tools/tas-ado.py +328 -25
  83. package/.tas/tools/tas-github.py +339 -0
  84. package/README.md +142 -57
  85. package/bin/cli.js +90 -90
  86. package/lib/adapters/antigravity.js +131 -131
  87. package/lib/adapters/claude-code.js +71 -35
  88. package/lib/adapters/codex.js +157 -157
  89. package/lib/adapters/cursor.js +80 -80
  90. package/lib/adapters/index.js +20 -20
  91. package/lib/adapters/utils.js +81 -81
  92. package/lib/deleted-files.json +7 -0
  93. package/lib/install.js +546 -543
  94. package/package.json +2 -2
  95. package/.tas/README.md +0 -334
  96. package/.tas/commands/tas-epic.md +0 -35
  97. package/.tas/commands/tas-story.md +0 -91
  98. package/.tas/rules/common/story-done.md +0 -30
  99. package/.tas/templates/Epic.md +0 -46
  100. package/.tas/templates/Story.md +0 -90
@@ -1,517 +1,517 @@
1
- # API Design Patterns
2
-
3
- Conventions and best practices for designing consistent, developer-friendly REST APIs.
4
-
5
- ## When to Apply
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), inconsistent with concurrent inserts
207
-
208
- ### Cursor-Based (Scalable)
209
-
210
- ```
211
- GET /api/v1/users?cursor=eyJpZCI6MTIzfQ&limit=20
212
-
213
- # Implementation
214
- SELECT * FROM users
215
- WHERE id > :cursor_id
216
- ORDER BY id ASC
217
- LIMIT 21; -- fetch one extra to determine has_next
218
- ```
219
-
220
- ```json
221
- {
222
- "data": [...],
223
- "meta": {
224
- "has_next": true,
225
- "next_cursor": "eyJpZCI6MTQzfQ"
226
- }
227
- }
228
- ```
229
-
230
- **Pros:** Consistent performance regardless of position, stable with concurrent inserts
231
- **Cons:** Cannot jump to arbitrary page, cursor is opaque
232
-
233
- ### When to Use Which
234
-
235
- | Use Case | Pagination Type |
236
- |----------|----------------|
237
- | Admin dashboards, small datasets (<10K) | Offset |
238
- | Infinite scroll, feeds, large datasets | Cursor |
239
- | Public APIs | Cursor (default) with offset (optional) |
240
- | Search results | Offset (users expect page numbers) |
241
-
242
- ## Filtering, Sorting, and Search
243
-
244
- ### Filtering
245
-
246
- ```
247
- # Simple equality
248
- GET /api/v1/orders?status=active&customer_id=abc-123
249
-
250
- # Comparison operators (use bracket notation)
251
- GET /api/v1/products?price[gte]=10&price[lte]=100
252
- GET /api/v1/orders?created_at[after]=2025-01-01
253
-
254
- # Multiple values (comma-separated)
255
- GET /api/v1/products?category=electronics,clothing
256
-
257
- # Nested fields (dot notation)
258
- GET /api/v1/orders?customer.country=US
259
- ```
260
-
261
- ### Sorting
262
-
263
- ```
264
- # Single field (prefix - for descending)
265
- GET /api/v1/products?sort=-created_at
266
-
267
- # Multiple fields (comma-separated)
268
- GET /api/v1/products?sort=-featured,price,-created_at
269
- ```
270
-
271
- ### Full-Text Search
272
-
273
- ```
274
- # Search query parameter
275
- GET /api/v1/products?q=wireless+headphones
276
-
277
- # Field-specific search
278
- GET /api/v1/users?email=alice
279
- ```
280
-
281
- ### Sparse Fieldsets
282
-
283
- ```
284
- # Return only specified fields (reduces payload)
285
- GET /api/v1/users?fields=id,name,email
286
- GET /api/v1/orders?fields=id,total,status&include=customer.name
287
- ```
288
-
289
- ## Authentication and Authorization
290
-
291
- ### Token-Based Auth
292
-
293
- ```
294
- # Bearer token in Authorization header
295
- GET /api/v1/users
296
- Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
297
-
298
- # API key (for server-to-server)
299
- GET /api/v1/data
300
- X-API-Key: sk_live_abc123
301
- ```
302
-
303
- ### Authorization Patterns
304
-
305
- ```typescript
306
- // Resource-level: check ownership
307
- app.get("/api/v1/orders/:id", async (req, res) => {
308
- const order = await Order.findById(req.params.id);
309
- if (!order) return res.status(404).json({ error: { code: "not_found" } });
310
- if (order.userId !== req.user.id) return res.status(403).json({ error: { code: "forbidden" } });
311
- return res.json({ data: order });
312
- });
313
-
314
- // Role-based: check permissions
315
- app.delete("/api/v1/users/:id", requireRole("admin"), async (req, res) => {
316
- await User.delete(req.params.id);
317
- return res.status(204).send();
318
- });
319
- ```
320
-
321
- ## Rate Limiting
322
-
323
- ### Headers
324
-
325
- ```
326
- HTTP/1.1 200 OK
327
- X-RateLimit-Limit: 100
328
- X-RateLimit-Remaining: 95
329
- X-RateLimit-Reset: 1640000000
330
-
331
- # When exceeded
332
- HTTP/1.1 429 Too Many Requests
333
- Retry-After: 60
334
- {
335
- "error": {
336
- "code": "rate_limit_exceeded",
337
- "message": "Rate limit exceeded. Try again in 60 seconds."
338
- }
339
- }
340
- ```
341
-
342
- ### Rate Limit Tiers
343
-
344
- | Tier | Limit | Window | Use Case |
345
- |------|-------|--------|----------|
346
- | Anonymous | 30/min | Per IP | Public endpoints |
347
- | Authenticated | 100/min | Per user | Standard API access |
348
- | Premium | 1000/min | Per API key | Paid API plans |
349
- | Internal | 10000/min | Per service | Service-to-service |
350
-
351
- ## Versioning
352
-
353
- ### URL Path Versioning (Recommended)
354
-
355
- ```
356
- /api/v1/users
357
- /api/v2/users
358
- ```
359
-
360
- **Pros:** Explicit, easy to route, cacheable
361
- **Cons:** URL changes between versions
362
-
363
- ### Header Versioning
364
-
365
- ```
366
- GET /api/users
367
- Accept: application/vnd.myapp.v2+json
368
- ```
369
-
370
- **Pros:** Clean URLs
371
- **Cons:** Harder to test, easy to forget
372
-
373
- ### Versioning Strategy
374
-
375
- ```
376
- 1. Start with /api/v1/ — don't version until you need to
377
- 2. Maintain at most 2 active versions (current + previous)
378
- 3. Deprecation timeline:
379
- - Announce deprecation (6 months notice for public APIs)
380
- - Add Sunset header: Sunset: Sat, 01 Jan 2026 00:00:00 GMT
381
- - Return 410 Gone after sunset date
382
- 4. Non-breaking changes don't need a new version:
383
- - Adding new fields to responses
384
- - Adding new optional query parameters
385
- - Adding new endpoints
386
- 5. Breaking changes require a new version:
387
- - Removing or renaming fields
388
- - Changing field types
389
- - Changing URL structure
390
- - Changing authentication method
391
- ```
392
-
393
- ## Implementation Patterns
394
-
395
- ### TypeScript (Next.js API Route)
396
-
397
- ```typescript
398
- import { z } from "zod";
399
- import { NextRequest, NextResponse } from "next/server";
400
-
401
- const createUserSchema = z.object({
402
- email: z.string().email(),
403
- name: z.string().min(1).max(100),
404
- });
405
-
406
- export async function POST(req: NextRequest) {
407
- const body = await req.json();
408
- const parsed = createUserSchema.safeParse(body);
409
-
410
- if (!parsed.success) {
411
- return NextResponse.json({
412
- error: {
413
- code: "validation_error",
414
- message: "Request validation failed",
415
- details: parsed.error.issues.map(i => ({
416
- field: i.path.join("."),
417
- message: i.message,
418
- code: i.code,
419
- })),
420
- },
421
- }, { status: 422 });
422
- }
423
-
424
- const user = await createUser(parsed.data);
425
-
426
- return NextResponse.json(
427
- { data: user },
428
- {
429
- status: 201,
430
- headers: { Location: `/api/v1/users/${user.id}` },
431
- },
432
- );
433
- }
434
- ```
435
-
436
- ### Python (Django REST Framework)
437
-
438
- ```python
439
- from rest_framework import serializers, viewsets, status
440
- from rest_framework.response import Response
441
-
442
- class CreateUserSerializer(serializers.Serializer):
443
- email = serializers.EmailField()
444
- name = serializers.CharField(max_length=100)
445
-
446
- class UserSerializer(serializers.ModelSerializer):
447
- class Meta:
448
- model = User
449
- fields = ["id", "email", "name", "created_at"]
450
-
451
- class UserViewSet(viewsets.ModelViewSet):
452
- serializer_class = UserSerializer
453
- permission_classes = [IsAuthenticated]
454
-
455
- def get_serializer_class(self):
456
- if self.action == "create":
457
- return CreateUserSerializer
458
- return UserSerializer
459
-
460
- def create(self, request):
461
- serializer = CreateUserSerializer(data=request.data)
462
- serializer.is_valid(raise_exception=True)
463
- user = UserService.create(**serializer.validated_data)
464
- return Response(
465
- {"data": UserSerializer(user).data},
466
- status=status.HTTP_201_CREATED,
467
- headers={"Location": f"/api/v1/users/{user.id}"},
468
- )
469
- ```
470
-
471
- ### Go (net/http)
472
-
473
- ```go
474
- func (h *UserHandler) CreateUser(w http.ResponseWriter, r *http.Request) {
475
- var req CreateUserRequest
476
- if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
477
- writeError(w, http.StatusBadRequest, "invalid_json", "Invalid request body")
478
- return
479
- }
480
-
481
- if err := req.Validate(); err != nil {
482
- writeError(w, http.StatusUnprocessableEntity, "validation_error", err.Error())
483
- return
484
- }
485
-
486
- user, err := h.service.Create(r.Context(), req)
487
- if err != nil {
488
- switch {
489
- case errors.Is(err, domain.ErrEmailTaken):
490
- writeError(w, http.StatusConflict, "email_taken", "Email already registered")
491
- default:
492
- writeError(w, http.StatusInternalServerError, "internal_error", "Internal error")
493
- }
494
- return
495
- }
496
-
497
- w.Header().Set("Location", fmt.Sprintf("/api/v1/users/%s", user.ID))
498
- writeJSON(w, http.StatusCreated, map[string]any{"data": user})
499
- }
500
- ```
501
-
502
- ## API Design Checklist
503
-
504
- Before shipping a new endpoint:
505
-
506
- - [ ] Resource URL follows naming conventions (plural, kebab-case, no verbs)
507
- - [ ] Correct HTTP method used (GET for reads, POST for creates, etc.)
508
- - [ ] Appropriate status codes returned (not 200 for everything)
509
- - [ ] Input validated with schema (Zod, Pydantic, Bean Validation)
510
- - [ ] Error responses follow standard format with codes and messages
511
- - [ ] Pagination implemented for list endpoints (cursor or offset)
512
- - [ ] Authentication required (or explicitly marked as public)
513
- - [ ] Authorization checked (user can only access their own resources)
514
- - [ ] Rate limiting configured
515
- - [ ] Response does not leak internal details (stack traces, SQL errors)
516
- - [ ] Consistent naming with existing endpoints (camelCase vs snake_case)
517
- - [ ] Documented (OpenAPI/Swagger spec updated)
1
+ # API Design Patterns
2
+
3
+ Conventions and best practices for designing consistent, developer-friendly REST APIs.
4
+
5
+ ## When to Apply
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), inconsistent with concurrent inserts
207
+
208
+ ### Cursor-Based (Scalable)
209
+
210
+ ```
211
+ GET /api/v1/users?cursor=eyJpZCI6MTIzfQ&limit=20
212
+
213
+ # Implementation
214
+ SELECT * FROM users
215
+ WHERE id > :cursor_id
216
+ ORDER BY id ASC
217
+ LIMIT 21; -- fetch one extra to determine has_next
218
+ ```
219
+
220
+ ```json
221
+ {
222
+ "data": [...],
223
+ "meta": {
224
+ "has_next": true,
225
+ "next_cursor": "eyJpZCI6MTQzfQ"
226
+ }
227
+ }
228
+ ```
229
+
230
+ **Pros:** Consistent performance regardless of position, stable with concurrent inserts
231
+ **Cons:** Cannot jump to arbitrary page, cursor is opaque
232
+
233
+ ### When to Use Which
234
+
235
+ | Use Case | Pagination Type |
236
+ |----------|----------------|
237
+ | Admin dashboards, small datasets (<10K) | Offset |
238
+ | Infinite scroll, feeds, large datasets | Cursor |
239
+ | Public APIs | Cursor (default) with offset (optional) |
240
+ | Search results | Offset (users expect page numbers) |
241
+
242
+ ## Filtering, Sorting, and Search
243
+
244
+ ### Filtering
245
+
246
+ ```
247
+ # Simple equality
248
+ GET /api/v1/orders?status=active&customer_id=abc-123
249
+
250
+ # Comparison operators (use bracket notation)
251
+ GET /api/v1/products?price[gte]=10&price[lte]=100
252
+ GET /api/v1/orders?created_at[after]=2025-01-01
253
+
254
+ # Multiple values (comma-separated)
255
+ GET /api/v1/products?category=electronics,clothing
256
+
257
+ # Nested fields (dot notation)
258
+ GET /api/v1/orders?customer.country=US
259
+ ```
260
+
261
+ ### Sorting
262
+
263
+ ```
264
+ # Single field (prefix - for descending)
265
+ GET /api/v1/products?sort=-created_at
266
+
267
+ # Multiple fields (comma-separated)
268
+ GET /api/v1/products?sort=-featured,price,-created_at
269
+ ```
270
+
271
+ ### Full-Text Search
272
+
273
+ ```
274
+ # Search query parameter
275
+ GET /api/v1/products?q=wireless+headphones
276
+
277
+ # Field-specific search
278
+ GET /api/v1/users?email=alice
279
+ ```
280
+
281
+ ### Sparse Fieldsets
282
+
283
+ ```
284
+ # Return only specified fields (reduces payload)
285
+ GET /api/v1/users?fields=id,name,email
286
+ GET /api/v1/orders?fields=id,total,status&include=customer.name
287
+ ```
288
+
289
+ ## Authentication and Authorization
290
+
291
+ ### Token-Based Auth
292
+
293
+ ```
294
+ # Bearer token in Authorization header
295
+ GET /api/v1/users
296
+ Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
297
+
298
+ # API key (for server-to-server)
299
+ GET /api/v1/data
300
+ X-API-Key: sk_live_abc123
301
+ ```
302
+
303
+ ### Authorization Patterns
304
+
305
+ ```typescript
306
+ // Resource-level: check ownership
307
+ app.get("/api/v1/orders/:id", async (req, res) => {
308
+ const order = await Order.findById(req.params.id);
309
+ if (!order) return res.status(404).json({ error: { code: "not_found" } });
310
+ if (order.userId !== req.user.id) return res.status(403).json({ error: { code: "forbidden" } });
311
+ return res.json({ data: order });
312
+ });
313
+
314
+ // Role-based: check permissions
315
+ app.delete("/api/v1/users/:id", requireRole("admin"), async (req, res) => {
316
+ await User.delete(req.params.id);
317
+ return res.status(204).send();
318
+ });
319
+ ```
320
+
321
+ ## Rate Limiting
322
+
323
+ ### Headers
324
+
325
+ ```
326
+ HTTP/1.1 200 OK
327
+ X-RateLimit-Limit: 100
328
+ X-RateLimit-Remaining: 95
329
+ X-RateLimit-Reset: 1640000000
330
+
331
+ # When exceeded
332
+ HTTP/1.1 429 Too Many Requests
333
+ Retry-After: 60
334
+ {
335
+ "error": {
336
+ "code": "rate_limit_exceeded",
337
+ "message": "Rate limit exceeded. Try again in 60 seconds."
338
+ }
339
+ }
340
+ ```
341
+
342
+ ### Rate Limit Tiers
343
+
344
+ | Tier | Limit | Window | Use Case |
345
+ |------|-------|--------|----------|
346
+ | Anonymous | 30/min | Per IP | Public endpoints |
347
+ | Authenticated | 100/min | Per user | Standard API access |
348
+ | Premium | 1000/min | Per API key | Paid API plans |
349
+ | Internal | 10000/min | Per service | Service-to-service |
350
+
351
+ ## Versioning
352
+
353
+ ### URL Path Versioning (Recommended)
354
+
355
+ ```
356
+ /api/v1/users
357
+ /api/v2/users
358
+ ```
359
+
360
+ **Pros:** Explicit, easy to route, cacheable
361
+ **Cons:** URL changes between versions
362
+
363
+ ### Header Versioning
364
+
365
+ ```
366
+ GET /api/users
367
+ Accept: application/vnd.myapp.v2+json
368
+ ```
369
+
370
+ **Pros:** Clean URLs
371
+ **Cons:** Harder to test, easy to forget
372
+
373
+ ### Versioning Strategy
374
+
375
+ ```
376
+ 1. Start with /api/v1/ — don't version until you need to
377
+ 2. Maintain at most 2 active versions (current + previous)
378
+ 3. Deprecation timeline:
379
+ - Announce deprecation (6 months notice for public APIs)
380
+ - Add Sunset header: Sunset: Sat, 01 Jan 2026 00:00:00 GMT
381
+ - Return 410 Gone after sunset date
382
+ 4. Non-breaking changes don't need a new version:
383
+ - Adding new fields to responses
384
+ - Adding new optional query parameters
385
+ - Adding new endpoints
386
+ 5. Breaking changes require a new version:
387
+ - Removing or renaming fields
388
+ - Changing field types
389
+ - Changing URL structure
390
+ - Changing authentication method
391
+ ```
392
+
393
+ ## Implementation Patterns
394
+
395
+ ### TypeScript (Next.js API Route)
396
+
397
+ ```typescript
398
+ import { z } from "zod";
399
+ import { NextRequest, NextResponse } from "next/server";
400
+
401
+ const createUserSchema = z.object({
402
+ email: z.string().email(),
403
+ name: z.string().min(1).max(100),
404
+ });
405
+
406
+ export async function POST(req: NextRequest) {
407
+ const body = await req.json();
408
+ const parsed = createUserSchema.safeParse(body);
409
+
410
+ if (!parsed.success) {
411
+ return NextResponse.json({
412
+ error: {
413
+ code: "validation_error",
414
+ message: "Request validation failed",
415
+ details: parsed.error.issues.map(i => ({
416
+ field: i.path.join("."),
417
+ message: i.message,
418
+ code: i.code,
419
+ })),
420
+ },
421
+ }, { status: 422 });
422
+ }
423
+
424
+ const user = await createUser(parsed.data);
425
+
426
+ return NextResponse.json(
427
+ { data: user },
428
+ {
429
+ status: 201,
430
+ headers: { Location: `/api/v1/users/${user.id}` },
431
+ },
432
+ );
433
+ }
434
+ ```
435
+
436
+ ### Python (Django REST Framework)
437
+
438
+ ```python
439
+ from rest_framework import serializers, viewsets, status
440
+ from rest_framework.response import Response
441
+
442
+ class CreateUserSerializer(serializers.Serializer):
443
+ email = serializers.EmailField()
444
+ name = serializers.CharField(max_length=100)
445
+
446
+ class UserSerializer(serializers.ModelSerializer):
447
+ class Meta:
448
+ model = User
449
+ fields = ["id", "email", "name", "created_at"]
450
+
451
+ class UserViewSet(viewsets.ModelViewSet):
452
+ serializer_class = UserSerializer
453
+ permission_classes = [IsAuthenticated]
454
+
455
+ def get_serializer_class(self):
456
+ if self.action == "create":
457
+ return CreateUserSerializer
458
+ return UserSerializer
459
+
460
+ def create(self, request):
461
+ serializer = CreateUserSerializer(data=request.data)
462
+ serializer.is_valid(raise_exception=True)
463
+ user = UserService.create(**serializer.validated_data)
464
+ return Response(
465
+ {"data": UserSerializer(user).data},
466
+ status=status.HTTP_201_CREATED,
467
+ headers={"Location": f"/api/v1/users/{user.id}"},
468
+ )
469
+ ```
470
+
471
+ ### Go (net/http)
472
+
473
+ ```go
474
+ func (h *UserHandler) CreateUser(w http.ResponseWriter, r *http.Request) {
475
+ var req CreateUserRequest
476
+ if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
477
+ writeError(w, http.StatusBadRequest, "invalid_json", "Invalid request body")
478
+ return
479
+ }
480
+
481
+ if err := req.Validate(); err != nil {
482
+ writeError(w, http.StatusUnprocessableEntity, "validation_error", err.Error())
483
+ return
484
+ }
485
+
486
+ user, err := h.service.Create(r.Context(), req)
487
+ if err != nil {
488
+ switch {
489
+ case errors.Is(err, domain.ErrEmailTaken):
490
+ writeError(w, http.StatusConflict, "email_taken", "Email already registered")
491
+ default:
492
+ writeError(w, http.StatusInternalServerError, "internal_error", "Internal error")
493
+ }
494
+ return
495
+ }
496
+
497
+ w.Header().Set("Location", fmt.Sprintf("/api/v1/users/%s", user.ID))
498
+ writeJSON(w, http.StatusCreated, map[string]any{"data": user})
499
+ }
500
+ ```
501
+
502
+ ## API Design Checklist
503
+
504
+ Before shipping a new endpoint:
505
+
506
+ - [ ] Resource URL follows naming conventions (plural, kebab-case, no verbs)
507
+ - [ ] Correct HTTP method used (GET for reads, POST for creates, etc.)
508
+ - [ ] Appropriate status codes returned (not 200 for everything)
509
+ - [ ] Input validated with schema (Zod, Pydantic, Bean Validation)
510
+ - [ ] Error responses follow standard format with codes and messages
511
+ - [ ] Pagination implemented for list endpoints (cursor or offset)
512
+ - [ ] Authentication required (or explicitly marked as public)
513
+ - [ ] Authorization checked (user can only access their own resources)
514
+ - [ ] Rate limiting configured
515
+ - [ ] Response does not leak internal details (stack traces, SQL errors)
516
+ - [ ] Consistent naming with existing endpoints (camelCase vs snake_case)
517
+ - [ ] Documented (OpenAPI/Swagger spec updated)