Dev Playbook
Conventions

API Design

RESTful API design principles and standards.

URL Structure

https://api.example.com/api/{resource}
  • Plural nouns for resources: /courses, /users, /enrollments
  • kebab-case for multi-word: /course-categories, /audit-logs
  • No verbs in URLs — use HTTP methods instead
  • Nesting for relationships: /courses/{id}/students
  • Max 2 levels of nesting. Beyond that, use query params or top-level resource.

HTTP Methods

MethodPurposeIdempotentRequest BodyResponse
GETRead resource(s)YesNo200 + data
POSTCreate resourceNoYes201 + created resource
PUTFull updateYesYes200 + updated resource
PATCHPartial updateYesYes (partial)200 + updated resource
DELETERemove resourceYesNo204 (no content)

Response Format

Success (single resource)

{
  "id": "abc-123",
  "title": "Introduction to Psychology",
  "createdAt": "2025-03-10T14:30:00Z"
}

Success (collection)

{
  "data": [...],
  "pagination": {
    "page": 1,
    "pageSize": 20,
    "totalPages": 5,
    "totalCount": 98
  }
}

Error

See api-error-format.md for the standard error format.

Pagination

Offset-based (default for most use cases):

GET /api/courses?page=2&pageSize=20
ParameterDefaultMaxDescription
page1Page number (1-based)
pageSize20100Items per page

Response includes pagination metadata in the response body (see above).

Cursor-based (for real-time feeds, infinite scroll):

GET /api/notifications?cursor=eyJpZCI6MTIzfQ&limit=20

Use cursor-based when:

  • Data changes frequently (new items inserted)
  • You need consistent pagination without duplicates
  • Large datasets where OFFSET is slow

Filtering

Use query parameters:

GET /api/courses?status=active&departmentId=5&search=psychology
  • Exact match: ?status=active
  • Search/partial: ?search=psych (searches relevant text fields)
  • Date range: ?createdAfter=2025-01-01&createdBefore=2025-12-31
  • Multiple values: ?status=active,archived (comma-separated)

Sorting

GET /api/courses?sortBy=createdAt&sortOrder=desc
ParameterValuesDefault
sortByField namecreatedAt
sortOrderasc, descdesc

For multiple sort fields: ?sortBy=status,-createdAt (prefix - for descending).

Versioning

Prefer URL path versioning when breaking changes are needed:

/api/v1/courses
/api/v2/courses

Rules:

  • Don't version from day one — start with /api/courses
  • Add versioning only when you make a breaking change
  • Support the previous version for a deprecation period
  • Document the migration path

Rate Limiting

Return these headers on every response:

X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1620000000

When exceeded, return 429 Too Many Requests with Retry-After header.

Common Patterns

Bulk Operations

POST /api/courses/bulk-delete
Body: { "ids": ["abc", "def", "ghi"] }

Actions (non-CRUD operations)

POST /api/courses/{id}/publish
POST /api/courses/{id}/archive
POST /api/users/{id}/deactivate

Health Check

GET /api/health          → 200 { "status": "healthy" }
GET /api/health/ready    → 200 or 503 (checks dependencies)

Response Codes Cheat Sheet

CodeWhen
200 OKSuccessful GET, PUT, PATCH
201 CreatedSuccessful POST (include Location header)
204 No ContentSuccessful DELETE
400 Bad RequestValidation error, malformed request
401 UnauthorizedMissing or invalid authentication
403 ForbiddenAuthenticated but not authorized
404 Not FoundResource doesn't exist
409 ConflictBusiness rule violation (duplicate, capacity full)
422 Unprocessable EntityValid JSON but semantically wrong
429 Too Many RequestsRate limit exceeded
500 Internal Server ErrorUnhandled server error (never expose details)

On this page