Back to Networking
Networking
hard
mid

What are good design and naming conventions for REST APIs?

REST: resources at URLs, HTTP verbs for actions (GET safe/idempotent, POST create, PUT/PATCH update, DELETE), status codes (2xx/3xx/4xx/5xx) with semantic meaning, JSON bodies, versioning (URL or header), pagination (cursor preferred), filtering/sort via query params, HATEOAS (rarely fully adopted). Idempotency keys for unsafe retries.

6 min read·~25 min to think through

REST is a set of conventions for HTTP APIs around resources, verbs, and status codes. Most production APIs are "RESTish" — they follow most conventions and skip the ideological parts (HATEOAS, hypermedia). What matters is predictability.

1. Resources, not actions

URLs name nouns, not verbs:

ts
GET    /users            # list
GET    /users/42         # one
POST   /users            # create
PUT    /users/42         # replace
PATCH  /users/42         # partial update
DELETE /users/42         # delete

/getUser?id=42 and /deleteUser are RPC-style, not REST.

2. HTTP verbs and their guarantees

VerbSafeIdempotentBody
GETno
HEADno
OPTIONSno
PUTyes
DELETEno
POSTyes
PATCHnot requiredyes
  • Safe: doesn't modify state.
  • Idempotent: same request, same effect.
  • POST is not idempotent — that's why retries need an Idempotency-Key header for create operations (Stripe's pattern).

3. Status codes

  • 2xx success: 200 OK (with body), 201 Created (+ Location), 204 No Content.
  • 3xx redirect: 301 permanent, 304 not modified (caching).
  • 4xx client: 400 bad request (validation), 401 unauthorized (auth needed), 403 forbidden (no access), 404 not found, 409 conflict, 422 unprocessable, 429 too many requests.
  • 5xx server: 500, 502, 503 (use 503 for maintenance), 504.

Use the right code. 200 with { error: "..." } is a hostile API.

4. Versioning

  • URL path: /v1/users — most common, easy to route, easy to deprecate.
  • Header: Accept: application/vnd.myapi.v1+json — cleaner URLs, harder to debug.

Either is fine; pick one and be consistent. Avoid query-string versioning.

5. Pagination

  • Offset/limit: ?page=2&limit=20 — simple, breaks on inserts, expensive on deep pages.
  • Cursor: ?cursor=abc&limit=20 — stable under inserts, supports infinite scroll.
  • Always include nextCursor/prevCursor (or links) in the response.

6. Filtering, sorting, sparse fields

ts
GET /products?category=shoes&price[gte]=100&sort=-price&fields=id,name,price

Conventions vary; document them.

7. Errors

A consistent error envelope:

json
{ "error": { "code": "user_not_found", "message": "...", "details": {...} } }

JSON:API and Problem Details (RFC 7807) are standardized options.

8. Idempotency, retries, concurrency

  • Idempotency-Key for POST creates so retries don't duplicate.
  • ETag + If-Match for optimistic concurrency on updates.
  • Retry-After header on 429 / 503.

9. Auth

  • Bearer tokens in Authorization header.
  • OAuth 2 / OIDC for delegated auth.
  • API keys for server-to-server.
  • Never put secrets in URLs (logged everywhere).

10. Caching

  • Cache-Control, ETag, Last-Modified.
  • Conditional GET → 304.

11. Discoverability (HATEOAS)

The pure REST ideal: responses include links to next actions ("_links": { "self": "/users/42", "delete": "..." }). In practice, most production APIs skip this — clients hardcode URLs from a spec (OpenAPI). It's fine.

12. Common smells

  • Verbs in URLs (/getUsers, /deleteUser).
  • 200 for everything regardless of outcome.
  • Inconsistent error shapes.
  • Offset pagination for feeds where new items arrive.
  • No idempotency story on creates.

Interview framing

"REST is HTTP-shaped: resources at URLs (nouns, not verbs), verbs for actions (GET safe/idempotent, POST create — not idempotent, PUT/PATCH update, DELETE), status codes with semantic meaning. Real conventions matter: cursor pagination for feeds (offset breaks under inserts), Idempotency-Key on POST creates so retries don't duplicate, a consistent error envelope, ETag/If-Match for optimistic concurrency, URL-path versioning, and proper use of 401 vs 403 vs 404. Full HATEOAS is rare in production — clients work from OpenAPI specs instead, and that's fine."

Follow-up questions

  • Why isn't POST idempotent and how do you make creates retry-safe?
  • Cursor vs offset pagination — when does the difference matter?
  • 401 vs 403 vs 404 — when do you use each?
  • How does ETag-based optimistic concurrency work?

Common mistakes

  • Verbs in URLs.
  • 200 with error body.
  • Offset pagination on a feed.
  • No idempotency on POST creates.
  • Different error shapes per endpoint.

Performance considerations

  • Caching headers + 304 conditional responses cut bandwidth. Compression (Brotli/gzip). Avoid n+1 on the client (batch endpoints or GraphQL).

Edge cases

  • PUT vs PATCH — replace vs partial update semantics.
  • DELETE returning 404 on a second call (idempotent? still 404).
  • Rate limits — 429 + Retry-After.

Real-world examples

  • Stripe, GitHub, Twilio — well-known examples of well-designed REST.

Senior engineer discussion

Seniors get the unsexy parts right: idempotency, error shape consistency, cursor pagination, ETag concurrency, accurate status codes. They prefer 'predictable' over 'pure REST' and pick conventions from a spec (OpenAPI) over hand-rolled documentation.

Related questions