Skip to content

REST API Design — Detailed#

Resource model#

/orders                  collection
/orders/{id}             one order
/orders/{id}/items       sub-collection
/orders/{id}/items/{lid} one item
/customers/{id}/orders   nested view of a customer's orders

Nouns, plural. URLs are stable; verbs are HTTP.

Verb cheat sheet#

Verb Idempotent Safe Used for
GET yes yes read
HEAD yes yes metadata only
OPTIONS yes yes CORS / capabilities
PUT yes no replace whole resource
DELETE yes no remove
POST no no create / non-idempotent action
PATCH not by default no partial update

"Idempotent" = same effect if repeated → safe to retry on network errors.

Status codes you'll actually use#

Code Meaning
200 OK read / non-creating success
201 Created new resource; Location: header points at it
202 Accepted async work queued
204 No Content success with empty body (DELETE)
400 Bad Request validation / parse error
401 Unauthorized missing or invalid auth
403 Forbidden authenticated but not allowed
404 Not Found resource doesn't exist
405 Method Not Allowed wrong verb for this URL
409 Conflict optimistic concurrency / business conflict
410 Gone sunset version
422 Unprocessable well-formed but semantically invalid
429 Too Many Requests rate limited; Retry-After
500 Internal Server Error bug
502/503/504 gateway / upstream / timeout

Error envelope (RFC 7807 Problem Details)#

{
  "type": "https://errors.example.com/insufficient_funds",
  "title": "Insufficient funds",
  "status": 422,
  "code": "INSUFFICIENT_FUNDS",
  "detail": "Account 42 has balance 5, requested withdrawal 100",
  "instance": "/withdrawals/abc-123",
  "trace_id": "0a8b...",
  "errors": [
    {"field": "amount", "code": "TOO_LARGE"}
  ]
}

Stable code for programmatic handling; trace_id so support can find the log line.

Idempotency#

sequenceDiagram
  participant C as Client
  participant S as Server
  participant DB as Idempotency table
  C->>S: POST /charges + Idempotency-Key: abc
  S->>DB: lookup abc
  alt new
    DB-->>S: not found
    S->>S: do work
    S->>DB: store (abc, response, 200)
    S-->>C: 200 + result
  else seen
    DB-->>S: cached response
    S-->>C: same response
  end

Client supplies Idempotency-Key header. Server stores (key → response, status) for 24-48 h. See idempotency-retries.

Pagination#

Style Pros Cons
Offset (?offset=200&limit=50) trivial skips/duplicates when data changes
Page-based (?page=4) same as offset, prettier same drift
Cursor (?cursor=opaque) stable, scales random access hard
Time-based (?since=ts) natural for events gaps if events share ts

Use cursor by default; next_cursor in the response.

Filtering / sorting / sparse fieldsets#

GET /orders?status=PAID&min_total=100&sort=-created_at&fields=id,total

Keep filters typed (status=PAID not status="paid"). Sort prefixes: - desc.

Search vs lookup#

  • Lookup by id: /orders/{id} → 404 if absent.
  • Search: /orders?status=...&q=... → 200 with possibly empty list.

Bulk actions#

  • POST /orders/bulk with array.
  • Return per-item result with HTTP 200 + errors[] (don't fail the whole batch on one bad row).

Long-running operations#

POST /reports                       → 202 Accepted, Location: /operations/op_1
GET  /operations/op_1               → status: running | done | failed
GET  /operations/op_1/result        → final payload when done

Caching headers#

  • ETag for conditional GET (If-None-Match).
  • Last-Modified / If-Modified-Since.
  • Cache-Control: public, max-age=..., s-maxage=... for shared caches.

Authn / Authz#

  • Authentication: Bearer JWT or session cookie.
  • Authorization: per-resource checks (404 vs 403 — 404 leaks less).
  • mTLS for B2B.

Versioning#

See api-versioning-evolution.

Anti-patterns#

  • Verbs in URLs (/getOrder?id=...).
  • Returning 200 with { "error": "..." } — use proper status.
  • Mixing snake_case + camelCase in the same payload.
  • null for "field unchanged" in PATCH — use JSON Merge Patch or JSON Patch standard.
  • Exposing primary keys without context — prefix with type (ord_abc).

Richardson Maturity Model#

  • L0: tunnels everything over POST.
  • L1: resources at URLs.
  • L2: HTTP verbs + statuses (most APIs).
  • L3: HATEOAS — hypermedia links in responses (rare in practice).

L2 is the realistic target.

Glossary & fundamentals#

Concepts referenced in this design. Each row links to its canonical page; the tag column shows whether it is a high-level (HLD) or low-level (LLD) concept.

Tag Concept What it is Page
HLD LSM vs B-Tree engines WAL, memtable, SSTables, compaction storage-engines-lsm-btree
HLD Idempotency & retries safe re-execution, backoff + jitter idempotency-retries
HLD Service mesh sidecar mesh, mTLS, traffic policy service-mesh
HLD API versioning & evolution URI / header versioning, sunsetting api-versioning-evolution
LLD REST API design verbs, statuses, pagination, errors rest-api-design