Skip to content
Community content. Review instructions before giving them to an AI agent — treat modules like open-source code.

REST API Design Rules

Design consistent, predictable REST APIs: resource-oriented URL paths, correct status codes, a uniform error envelope, pagination/filtering/sorting conventions, versioning, and idempotency.

Mby @markdownersPublished August 21, 2026 · ~3 min read

0 downloads · Used by 0 stacks

A REST API is a contract other people's code depends on — every inconsistency (a verb in one path, a noun in another; 200 here, 204 there) is a support burden multiplied across every consumer forever. Optimize for predictability over cleverness.

Resource paths

  • Name paths after nouns, never verbs: POST /orders, not POST /createOrder — the HTTP method already carries the verb; a verb in the path duplicates it and produces inconsistent naming the moment two people design different endpoints.
  • Use plural nouns for collections (/users, /orders/{id}/items) consistently — never mix singular and plural across the same API.
  • Nest resources only one level deep for ownership (/orders/{id}/items), not the full hierarchy (/customers/{id}/orders/{id}/items/{id}) — deep nesting couples clients to a specific ownership chain that will eventually change; prefer flat resources with filter query params instead once nesting would exceed one level.
  • Use path parameters for resource identity (/orders/{id}) and query parameters for everything else (filtering, sorting, pagination) — never encode optional behavior into the path itself.

Status codes

  • Return 200 for a successful read/update, 201 (with a Location header) for a successful creation, 204 for a successful action with no response body, 400 for malformed requests, 401 for missing/invalid auth, 403 for authenticated-but-not-allowed, 404 for a resource that doesn't exist (or that the caller isn't allowed to know exists), 409 for a state conflict, 422 for semantically invalid input that parsed fine, and 429 for rate limiting — pick the specific code, never default everything to 200 with an error flag in the body.
  • Never return 200 for an error — a client that checks only the status code (the common case) will treat the response as success and corrupt its own state.

Error envelope

  • Return one consistent error shape across every endpoint (e.g. { error: { code, message, details? } }) — a client should be able to write one error-parsing function for the whole API, not one per endpoint.
  • Use a stable machine-readable code ("validation_error", "not_found") in addition to a human message — messages can change wording without breaking clients that switch on code.
  • Never leak internal detail (stack traces, SQL, internal ids) in error responses — log the detail server-side, return a sanitized message to the caller.

Pagination, filtering, sorting

  • Paginate every collection endpoint from day one, even when the initial dataset is small — retrofitting pagination onto a live client base is a breaking change; a small dataset today does not mean the endpoint stays small.
  • Prefer cursor-based pagination (?after=<cursor>) over offset-based (?page=2) for anything that can grow or mutate during iteration — offset pagination skips or duplicates rows when the underlying data changes between requests.
  • Use consistent, documented query param names for filtering (?status=active) and sorting (?sort=-created_at for descending) across every collection endpoint, not endpoint-specific conventions.

Versioning

  • Version the API explicitly from the first public release (/v1/orders or an Accept header version), even if only one version will ever exist — adding versioning later means every existing client is implicitly "v1" with no way to signal that.
  • Make breaking changes only in a new version; add optional fields or new endpoints within the current version freely — additive changes are not breaking, so they don't need a version bump.

Idempotency and partial updates

  • Make PUT and DELETE idempotent by construction (retrying the same request produces the same end state) — this is what lets clients safely retry on network failure without double-applying an action.
  • Support idempotency keys (a client-supplied Idempotency-Key header) on POST endpoints that create resources with side effects (charges, orders) — without one, a retried POST after a timeout creates a duplicate.
  • Use PATCH with a partial body for partial updates, not PUT with a full resource representation — PUT semantically means "replace the whole resource," and treating it as partial-update silently drops fields the client didn't include.
  • Document exactly which fields a PATCH accepts and whether omitted fields are left unchanged or reset to default — this is the single most common point of confusion between client and server implementations.
Badge

Link back to this module from your own README.

Get it on Markdowners
[![Get it on Markdowners](https://markdowners.com/mdstack-badge.svg)](https://markdowners.com/m/markdowners/rest-api-design-rules)

Comments (0)

Sign in to comment. Sign in

No comments yet. Be the first to add one.

Discussions about this module

No discussions about this module yet.

Start a discussion