Resources, verbs and status codes

What it is

The design rules of a RESTful API: name things rather than actions, use the protocol’s methods for what they were defined to mean, and report the outcome with an accurate status code. A resource is a noun with an address; a method says what to do with it; a status code says what happened. Following these three consistently is most of what makes an API pleasant to use.

Why it matters

An API is an interface with a long life, and its conventions are what let a client be written without reading a specification cover to cover. When the rules are followed, the same request shape works for a new resource and the same error handling works for a new failure. When they are broken — a retrieval that deletes, a creation that is a retrieval, an error that returns a success code — clients must special-case every endpoint, and the mistakes are expensive to correct once they are in use.

Core ideas

Name resources, not operations

An address should identify a thing, not an action on it. A collection address lists the things; a child address identifies one; a nested address identifies something owned by another thing. Actions that do not map onto a method can be modelled as a subordinate resource, such as a refund on a payment, rather than added as a verb to the path.

GET  /orders            -> list
GET  /orders/42         -> one order
POST /orders            -> create
GET  /orders/42/items   -> the items of one order

Use the methods for their defined meanings

A retrieval is safe and repeatable; creating is not; replacing the whole thing with a given address is repeatable; deleting is repeatable in effect. Intermediaries, retries and pre-fetchers all assume those meanings. An endpoint that changes state on a retrieval will be triggered by a link preview or a crawler, and one that creates a second record on retry will produce duplicates whenever a network hiccup occurs.

Status codes are the machine-readable outcome

A success means the request was understood and performed; a redirect means look elsewhere; a client error means the request itself is wrong and retrying will not help; a server error means the request may be fine and retrying might. Choosing between the specific codes within a family carries information the client can act on — a validation failure is different from an authentication failure, which is different from a missing resource.

A creation returns where the thing now lives

A creation that succeeds should identify the new resource in a header and usually return its representation. That is what lets a client proceed without a second request and without guessing an identifier. Returning only a success flag makes every client poll or construct addresses itself.

Idempotency is a property to design for, not a coincidence

A repeatable operation can be retried safely after a timeout; a non-repeatable one cannot. For creations that must be retried — a payment, an order — the client supplies a key and the server records it, so a second attempt with the same key returns the first result rather than doing the work twice. This is the difference between a system that survives a flaky network and one that charges twice.

Errors should be uniform and informative

Every failure should use the same shape, name the field or condition that failed, and include a code a client can branch on without parsing prose. A uniform error shape means one handler on the client; a per-endpoint shape means one per endpoint. It also makes logs and dashboards uniform, because the same code appears everywhere.

Practice

Model one domain as resources

Take a small domain with three nouns and one operation that does not fit a method, and write the addresses and methods for each.

Done when: every address identifies a thing, every method matches its defined meaning, and the odd operation has a resource of its own.

Add idempotency to a creation

Take a creation endpoint and add a client-supplied key so a repeated request returns the original result rather than creating a second record.

Done when: sending the same request twice with the same key produces one record and identical responses.

Common mistakes

  • Putting verbs in the path — the address stops identifying a thing; fix: model the operation as a resource or use the correct method.
  • Changing state on a retrieval — crawlers and pre-fetchers perform the change; fix: keep retrievals safe and use a method that means change.
  • Returning a success code for an error — every client needs a special case; fix: use the status family that matches the outcome.
  • Making a creation non-repeatable without a key — a retry duplicates the record; fix: accept an idempotency key and return the first result.
  • Inventing an error shape per endpoint — clients accumulate parsers; fix: one error shape with a branchable code.

Go deeper