Backend · Guide

REST APIs

Resources, status codes and versioning — and the OpenAPI contract that keeps clients honest.

— min read Backend

Nouns, Verbs And Meaning

REST is a set of conventions that let a client predict how an API behaves without reading much documentation. Its value is entirely in being unsurprising — a clever API is a bad API.

The core idea is that URLs identify resources (nouns) and HTTP methods supply the verbs. POST /createOrder works, but it throws away every guarantee the protocol offers: caches, retries and proxies all understand GET and PUT, and understand nothing about your verb.

MethodMeaningSafeIdempotent
GETReadYesYes
POSTCreate, or an actionNoNo
PUTReplace at a known idNoYes
PATCHPartial updateNoNot necessarily
DELETERemoveNoYes

Idempotent means repeating the call leaves the same state — which is what makes a client safe to retry after a timeout. For non-idempotent creates, accept an Idempotency-Key header and store the result against it; that single mechanism prevents the duplicate-charge class of bug.

Status Codes, Errors & Pagination

Status codes are the part clients actually branch on, so returning 200 with {"error": …} inside breaks every generic client, proxy and monitoring tool at once.

CodeMeans
200 / 201Done / created, with a Location header
202Accepted, still processing
400Malformed — do not retry unchanged
401 / 403Not authenticated / authenticated but not allowed
404 / 409No such resource / conflicts with current state
422Well formed but semantically invalid
429Rate limited — send Retry-After
500 / 503We broke / temporarily unavailable, retry later
// a machine-readable error body: code for logic, message for humans
{
  "error": {
    "code": "insufficient_funds",
    "message": "Balance is below the transfer amount.",
    "details": { "available": 1240, "requested": 5000 }
  }
}

Paginate every collection from day one. Offset paging is simple and drifts when rows are inserted mid-scan; cursor paging is stable and is what any endpoint over a growing table should use. Retrofitting pagination onto a live endpoint is a breaking change.

Versioning & Compatibility

Clients you do not control will keep calling the shape you shipped. The discipline is to make additive changes forever and version only when you genuinely cannot.

ChangeBreaking?
Adding an optional field to a responseNo — clients must ignore unknown fields
Adding an optional request parameterNo
Removing or renaming a fieldYes
Tightening validationYes — previously accepted calls now fail
Changing a type, unit or defaultYes, and the worst kind: silent
StrategyTrade-off
/v1/ in the pathObvious, cacheable, ugly — the common choice
Version headerCleaner URLs, easier to get wrong in a client
No versioning, additive onlyCleanest when you can hold the line
Every version you publish is one you support. Announce deprecation with a date, measure who is still calling the old one, and contact them — a version with no sunset plan is permanent.

OpenAPI & Contracts

An OpenAPI document describes the API in machine-readable form: paths, parameters, schemas, responses and auth. Written by hand or generated from code, it becomes the artefact everything else hangs off.

paths:
  /orders/{id}:
    get:
      summary: Fetch an order
      parameters:
        - { name: id, in: path, required: true, schema: { type: string } }
      responses:
        '200':
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Order' }
        '404':
          $ref: '#/components/responses/NotFound'
Gives youInstead of
Generated clients and server stubsHand-written HTTP calls per consumer
Request and response validationTrusting that the docs are current
Interactive documentationA wiki page from last year
Contract tests in CIFinding the break in production
Mock serversFront end blocked on the backend
A spec that is not enforced is documentation, and documentation drifts. Validate real responses against the schema in your test suite so the build fails when the code and the contract disagree.

Interview Questions

What does idempotent mean, and why does it matter?

Repeating the request leaves the same state. It is what makes a client safe to retry after a timeout — and why non-idempotent creates need an idempotency key to avoid duplicates.

Why not return 200 with an error body?

Status codes are what clients, proxies, caches and monitoring branch on. Hiding failure inside a success breaks all of them and makes error rates invisible.

Offset or cursor pagination?

Cursor for anything over a growing table — offset paging skips or repeats rows when data is inserted mid-scan. Add pagination from the start; retrofitting it is a breaking change.

Which API changes are breaking?

Removing or renaming fields, tightening validation, and changing a type, unit or default. Adding optional fields and parameters is safe if clients ignore unknown fields.

What does OpenAPI actually buy you?

Generated clients and stubs, request and response validation, live documentation, mock servers, and contract tests that fail the build when code and spec diverge.

PUT or PATCH?

PUT replaces the whole resource at a known id and is idempotent. PATCH applies a partial change and is only idempotent if the operations themselves are.

Quick Quiz

1. Which method is NOT idempotent?
2. A retried POST that creates twice is prevented by…
3. Cursor pagination is preferred because it…
4. Adding an optional response field is…
5. An OpenAPI spec is only trustworthy if…