Back to the map

Module 04 · API fundamentals · Lesson 3 of 15

REST API design

Resource naming, methods and status codes used as intended, consistent error bodies, and the handful of conventions that make a REST API predictable. Includes the mistakes that mark an API as home-grown.

CoreMust-knowMid 12 min Updated Nov 2026

Why it matters

A REST API that follows conventions can be learned in ten minutes by anyone who has used another one; an API that invents its own needs documentation for every endpoint and still surprises people. Conventions also let infrastructure help you: caches cache GETs, retries retry idempotent methods, monitoring groups by status class. Getting REST right is less about theory than about not undermining the tools that already understand HTTP.

The idea

REST (as practised) means: resources identified by URLs, HTTP methods for operations, status codes for outcomes, and representations (usually JSON) for state. The conventions below are what "RESTful" means in a code review.

Resources and URLs

Nouns, plural, hierarchical where ownership is real: /customers/{id}/orders/{orderId}. Avoid verbs in paths; the method is the verb. When an action does not fit CRUD (ship an order, refund a payment), model it as a sub-resource or a state change: POST /orders/{id}/shipments or PATCH /orders/{id} with status: shipped. Keep nesting to two levels; deeper paths are hard to use and usually mean the child deserves its own top-level collection.

Methods and status codes

OperationRequestSuccessCommon errors
ListGET /orders?status=open&limit=50200 with an array and paging info400 bad filter, 401, 403
ReadGET /orders/42200 with the resource404 not found
CreatePOST /orders (+ Idempotency-Key)201 with Location header and the resource400 malformed, 422 invalid, 409 duplicate key
ReplacePUT /orders/42200, or 204 with no body404, 409 version conflict
Partial updatePATCH /orders/42200 with the resource422 invalid field, 409 conflict
DeleteDELETE /orders/42204 (idempotent: a repeat is 204 or 404)404, 409 if not deletable in this state
Use the status code that says what happened. 200 with an error body defeats every tool that reads status codes.

Errors have one shape

Pick a single error envelope and use it everywhere: a stable machine-readable code, a human message, optional details with field paths, and a request id for support. RFC 9457 (problem details) is a ready-made standard. Clients branch on code; they never parse message.

Concurrency and caching

Return an ETag on reads and require If-Match on updates so two clients editing the same resource cannot silently overwrite each other (optimistic concurrency, 412 on mismatch). Set Cache-Control on GETs honestly: private, max-age=0 for per-user data, longer for public reference data. Support If-None-Match so clients revalidate cheaply with 304.

Client AClient BOrder 42ETag: v7 → v8PATCH If-Match: v7 → 200, now v8PATCH If-Match: v7 → 412 Precondition Failed
Optimistic concurrency with ETags: the second writer's stale version is rejected instead of overwriting the first.

Conventions that prevent surprises

  • Consistent casing (pick snake_case or camelCase and never mix), ISO 8601 timestamps in UTC, money as integer minor units plus a currency code, ids as opaque strings.
  • Filtering, sorting and pagination as query parameters with documented defaults and maximums (next lesson).
  • Partial responses or field selection (?fields=) when payloads are large; otherwise accept that REST over-fetches and move on.
  • A machine-readable spec (OpenAPI) that is the source of truth for docs, client generation and contract tests.
  • Hypermedia links (HATEOAS) are optional in practice; a Location header on create and next links on lists cover most real needs.

Trade-offs

Strict REST is predictable and tool-friendly and can feel verbose for action-heavy domains, where you end up modelling verbs as resources. Field selection and embedding reduce round trips but complicate caching and the server. Optimistic concurrency protects data and adds a header clients must learn to send. Each convention costs a little discipline and saves a lot of support.

In an interview

Design one endpoint fully rather than ten sketchily: "POST /orders with an Idempotency-Key, body validated against the schema, 201 with Location on success, 409 if the key was seen with a different body, 422 for business-rule failures with a code the client can act on. GET /orders/42 returns an ETag; PATCH requires If-Match." That demonstrates every convention in thirty seconds.

Check yourself

An API returns HTTP 200 for every request and puts {'success': false, 'error': '...'} in the body when something goes wrong. What does this break?

REST API design · Backplane