REST API Design Agent Rules
Project Context
You are designing and implementing REST APIs. APIs must be consistent, intuitive, versioned, and well-documented. Treat the API as a product — clients depend on its stability.
URL Structure & Resource Naming
- Use plural nouns for resource collections: `/users`, `/orders`, `/products`.
- Use nested paths for owned relationships: `/users/{userId}/addresses` (address belongs to user).
- Keep nesting shallow — maximum two levels. Use query parameters for deeper filtering instead of `/a/{id}/b/{id}/c`.
- Use `kebab-case` for multi-word path segments: `/user-profiles`, `/shipping-addresses`.
- Never use verbs in URLs: `/users/{id}/activate` not `/activateUser/{id}`.
- Use actions as sub-resources for non-CRUD operations: `POST /orders/{id}/cancel`, `POST /accounts/{id}/verify`.
- Use query parameters for filtering, sorting, and pagination: `GET /orders?status=pending&sort=created_at&order=desc`.
HTTP Methods & Semantics
- `GET` — read only, idempotent, safe — never modifies state.
- `POST` — create a new resource or trigger an action — not idempotent.
- `PUT` — full replacement of a resource — idempotent.
- `PATCH` — partial update applying specific field changes — use JSON Merge Patch (RFC 7396) or JSON Patch (RFC 6902).
- `DELETE` — remove a resource — idempotent (deleting twice returns 404 on the second call, not an error).
- Never use `GET` for operations with side effects.
HTTP Status Codes
- `200 OK` — successful GET, PUT, PATCH, DELETE with response body.
- `201 Created` — successful POST that creates a resource; include `Location` header pointing to the new resource.
- `202 Accepted` — request accepted for asynchronous processing; include a status polling URL.
- `204 No Content` — successful DELETE or PATCH with no response body.
- `400 Bad Request` — malformed request syntax, invalid field types, or validation errors.
- `401 Unauthorized` — missing or invalid authentication credentials.
- `403 Forbidden` — authenticated but not authorized for this operation.
- `404 Not Found` — resource does not exist; never use `403` to hide existence from unauthorized users.
- `409 Conflict` — duplicate resource, version mismatch, or state conflict.
- `422 Unprocessable Entity` — syntactically valid but semantically invalid: `email` field contains a non-email value.
- `429 Too Many Requests` — rate limit exceeded; include `Retry-After` header.
- `500 Internal Server Error` — unexpected server failure; never return stack traces in response bodies.
Response Format
- Use consistent JSON structure for all responses — wrap data in a top-level `data` key for single resources and collections.
- Include pagination metadata on collection responses: `meta: { page, perPage, total, nextCursor }`.
- Use `camelCase` for JSON property names — consistent with JavaScript client expectations.
- Use ISO 8601 format for all timestamps: `"2024-01-15T10:30:00Z"`.
- Use string representations for large integers (>53 bits) to avoid JavaScript precision loss.
- Return the full created or updated resource in `POST`, `PUT`, and `PATCH` responses — clients should not need a follow-up GET.
Error Response Schema
- Use a consistent error envelope: `{ "error": { "code": "VALIDATION_ERROR", "message": "...", "details": [...] } }`.
- Machine-readable `code` field for programmatic handling: `INSUFFICIENT_BALANCE`, `EMAIL_ALREADY_EXISTS`.
- Human-readable `message` field: clear, non-technical description safe to display to end users.
- `details` array for field-level validation errors: `[{ "field": "email", "code": "INVALID_FORMAT", "message": "Must be a valid email" }]`.
- Never leak stack traces, internal database errors, or server paths in error responses.
Pagination
- Use cursor-based pagination for large, frequently updated datasets: `?cursor=eyJpZCI6MTIzfQ&limit=20`.
- Use offset pagination for small, stable datasets or admin interfaces: `?page=2&per_page=20`.
- Always set a maximum page size (100) and a sensible default (20) — never allow unbounded results.
- Return `nextCursor` and `prevCursor` in pagination metadata; return `null` when at the boundary.
- Document the pagination strategy clearly — mixing cursors and offsets confuses API consumers.
Versioning
- Version via URL path prefix: `/api/v1/users`, `/api/v2/users` — the most discoverable approach.
- Increment major version only for breaking changes: removing fields, changing types, renaming endpoints.
- Keep previous major version active for at least 6 months after deprecating it.
- Return `Deprecation: Tue, 15 Oct 2024 00:00:00 GMT` and `Sunset: Mon, 15 Apr 2025 00:00:00 GMT` headers on deprecated endpoints.
- Never introduce breaking changes to a released version — add new endpoints or optional fields instead.
Authentication & Security
- Use `Authorization: Bearer <token>` header for all authenticated endpoints.
- Return identical `401` responses for invalid tokens and missing tokens — do not reveal which condition triggered it.
- Implement rate limiting on all public endpoints; use `X-RateLimit-Limit`, `X-RateLimit-Remaining`, and `X-RateLimit-Reset` headers.
- Validate and sanitize all path parameters, query parameters, and request bodies — treat all input as untrusted.
- Use HTTPS exclusively — never allow HTTP in production or staging.
OpenAPI Documentation
- Maintain an `openapi.yaml` or `openapi.json` spec alongside the implementation — generate from code when possible.
- Document every endpoint with `summary`, `description`, `parameters`, `requestBody`, and `responses`.
- Include example request and response bodies on every endpoint.
- Define reusable schemas in `components/schemas` — reference them with `$ref` rather than duplicating inline.
- Document authentication schemes in `components/securitySchemes` and apply `security:` at the operation level.
- Run `openapi-validator` in CI to catch spec errors and drift from the implementation.