Lesson 1 · Foundations · visual edition

What actually makes an API RESTful?

~7 min · one diagram per idea · retrieval practice at the end

Published

API RESTfulREST constraintsStatus codesHTTP methodsREST

Why this, first. “Is your API RESTful?” is the most common opener in a senior API interview — and the fastest way to sound mid-level is to answer “it’s JSON over HTTP with verbs.” REST is an architectural style: a named bundle of constraints defined by Roy Fielding. 1 Name the constraints and the rest of the interview answers itself.

REST is a style, not a format

defined by 6 constraints

measured on a maturity ladder

most APIs sit at Level 2

REST = THE STYLE YOU GET BY APPLYING SIX CONSTRAINTS REST architectural style 1 · Client–server UI and data evolve apart 2 · Stateless no server-side session 3 · Cacheable responses declare freshness 4 · Uniform interface one generic contract 5 · Layered system proxies & caches slot in 6 · Code-on-demand optional · rarely the point
An API is "RESTful" only to the degree it honours these. Each trades some freedom for a system-wide property — the why is what separates recall from understanding.

The six constraints (and the senior “why”)

Don’t recite the list — pair each with the property it buys. That trade is the answer interviewers are actually grading.

1 · Client–server Split UI from data storage so each side evolves independently.

2 · Stateless Every request carries all its context; no per-client session is kept. Any node serves any request, so you scale horizontally. Probed hardest.

3 · Cacheable Responses declare whether they can be cached — the foundation under ETags and CDNs.

4 · Uniform interface One generic contract: identified resources, manipulation via representations, self-descriptive messages, hypermedia. Decouples client from server.

5 · Layered system A client can’t tell origin from intermediary, so gateways and proxies slot in invisibly.

6 · Code-on-demand The server may ship runnable code to the client. The only optional constraint.

Statelessness is the constraint to lead with. Trace it forward: no server sessions → auth rides a token on every request (Bearer/JWT) → any node serves any request → you scale by adding boxes, not by sticky-routing users. One constraint, a whole architecture.

What “stateless” buys you

Each request is self-contained — it carries its own auth and context. The server keeps no memory between calls, so the load balancer is free to route the next request anywhere. 3

EACH REQUEST IS SELF-CONTAINED · NO SERVER SESSION Client attaches token + context to every request GET /me · Bearer … GET /me · Bearer … Load balancer Server node A holds no session ✓ Server node B holds no session ✓ Because no node "remembers" you, both requests are interchangeable — that is horizontal scale.
Statelessness is what lets the layered system work: any node, any time, any request.
✗ Myth: “REST means JSON over HTTP with the four CRUD verbs.”

✓ Truth: REST is a constraint set; JSON + verbs is the surface. Honour the constraints (esp. statelessness + uniform interface) or it isn’t REST.

The framing device: Richardson Maturity Model

Martin Fowler popularised Leonard Richardson’s maturity model 2 — a 0-to-3 ladder you can walk an interviewer up in thirty seconds.

THE MATURITY LADDER · CLIMB IT IN 30 SECONDS L0 · Swamp of POX one URI, one verb · RPC tunnel L1 · Resources many URIs (/orders/42) L2 · HTTP verbs methods + status carry meaning L3 · HATEOAS responses link next actions ← most real APIs deliberately stop here
L0 POX → L1 resources → L2 verbs → L3 hypermedia. The senior move isn't claiming Level 3 — it's knowing Level 2 is the pragmatic industry bar and saying why your API sits where it does on purpose.

Fielding himself argues that without hypermedia it isn’t really REST. 1 Naming that tension — then defending your pragmatic choice — is exactly the judgment they’re testing.

🧭 Memory rule: REST = a style of 6 constraints (client–server, stateless, cacheable, uniform interface, layered, code-on-demand[opt]); maturity is a 0→3 ladder (POX → resources → verbs → HATEOAS) and you live at L2 on purpose.

Retrieval practice

Close the lesson in your mind first, then answer from memory. Immediate feedback below.

Q1. Which best describes what makes an API "RESTful"?

Q2. REST's statelessness constraint means the server…

Q3. What lifts an API from Richardson Level 2 to Level 3?

Q4. The uniform interface constraint exists mainly to…

Rehearse the answer out loud

Interviewer: "What makes an API RESTful, and what's the difference between REST and 'JSON over HTTP'?" ~60 seconds, from memory — then reveal and compare.


“REST is an architectural style, not a wire format. ‘JSON over HTTP’ is just the surface; an API is RESTful to the degree it honours Fielding’s constraints— client–server, stateless, cacheable, uniform interface, layered system, and optional code-on-demand. The one I lead with is statelessness: no server session, so auth rides a token on every request and any node serves any request — that’s how we scale horizontally.

To place a given API, I use the Richardson Maturity Model: L0 is RPC-over-HTTP, L1 adds resource URIs, L2 uses verbs and status codes meaningfully, L3 adds hypermedia (HATEOAS). Ours is a deliberate Level 2 — true HATEOAS is rare because clients seldom exploit it. If we had many third-party clients we couldn’t redeploy, I’d push toward L3 to decouple them from our URL structure. Knowing why we sit at L2 is the point — that’s the difference between REST and just JSON over HTTP.”

I’m your teacher — ask me anything. Want me to trace statelessness into auth and scaling step by step, contrast Level 2 vs true HATEOAS with a concrete payload, or fire harder follow-ups (where the uniform interface costs you efficiency, when code-on-demand actually shows up)? Say the word.

Interview — pick your bar

Answer out loud in ~60s, then reveal. Core = recall · Senior = trade-offs & failure modes · Staff = synthesis under ambiguity · System Design = open design round (a different axis, not a harder level).

What is REST, precisely?

An architectural style defined by a set of constraints, coined by Roy Fielding — not a protocol, data format, or "JSON over HTTP." An API is "RESTful" only to the degree it honors the constraints.

Name the six architectural constraints.

Client–server, stateless, cacheable, uniform interface, layered system, and code-on-demand (the only optional one).

What is HATEOAS?

Hypermedia As The Engine Of Application State: responses include links advertising the valid next actions, so the client discovers what it can do rather than hard-coding URIs and workflow → the server can then change its URL structure without breaking clients.

Describe the Richardson Maturity Model.

A 0–3 ladder of how fully an API uses REST's ideas: L0 one URI / one verb (RPC-over-HTTP, "Swamp of POX"); L1 many resources, each a URI; L2 proper HTTP verbs + status codes; L3 hypermedia (HATEOAS). Most production "REST" APIs sit at L2.

Is REST the same thing as HTTP?

No. REST is a style; HTTP is a protocol that fits it very well. You can apply REST principles over other protocols, and you can use HTTP un-RESTfully — e.g. tunnelling everything through one POST endpoint (Richardson Level 0).

Why does statelessness help you scale — and what does it not forbid?

Any node can serve any request — no sticky sessions, no affinity — so you scale horizontally behind a load balancer and a dying node loses no session → it also improves visibility (each request is self-contained). It does not forbid databases or cookies: it forbids server-side session state between requests. Auth moves into a token (Bearer/JWT) sent every request.

If HATEOAS is part of REST, why is it so rare in real APIs?

Because the payoff rarely justifies the cost. Most clients are first-party and version-pinned — coded against known endpoints, not dynamically following links. Tooling/codegen expect fixed URLs, so the decoupling HATEOAS buys goes unused and teams stop at Level 2. The senior move is to know that and say so, not pretend everyone does L3.

REST vs GraphQL vs gRPC — when would you pick each?

REST: resource-oriented, leans on HTTP caching, broad reach, easy to evolve — great at the public edge. GraphQL: client shapes the query (kills over-/under-fetching), one endpoint for many varied clients — but HTTP caching is hard, plus query-cost/N+1 control. gRPC: low-latency internal service-to-service, typed contracts, streaming — but binary, less edge-friendly. Rule of thumb: REST at the edge, gRPC between internal services, GraphQL for rich multi-client read graphs.

Is a Level 2 API "really REST" by Fielding's own definition — and how do you defend it?

Strictly no — Fielding argues that without the hypermedia constraint it isn't truly REST. But Level 2 is what the industry pragmatically means by "REST." The strong answer names that tension explicitly, then defends the pragmatic choice for the given context (first-party version-pinned clients, codegen, fixed URLs) rather than dodging it.

When would you actually invest in HATEOAS / Level 3?

When you have many third-party clients you can't redeploy and must evolve URLs/workflows without breaking them; when the API models a state machine where available actions genuinely change (a payment that can be captured/refunded/voided — links express the legal transitions); or where discoverability is itself a product feature.

A team says "let's make our API RESTful." How do you advise them?

First ask why — what outcome (evolvability, caching, DX, broad reach)? Then assess current maturity. For most, the goal is a clean Level 2: good resource modeling, correct verbs/status codes, real cache headers, consistent errors. Pursue L3/HATEOAS only if uncontrollable third-party clients or a genuine state machine demand it. Don't chase REST purity for its own sake — tie every constraint back to a concrete benefit.

REST design-round framework — drive any "design a REST API" prompt through these, out loud:
  1. Model resources & URIs — nouns not verbs, collections vs items (/orders, /orders/{id}), nesting only for true ownership.
  2. Map methods & status codes — GET/POST/PUT/PATCH/DELETE to the right semantics; 2xx/4xx/5xx that mean what they say.
  3. Idempotency & caching — safe/idempotent verbs, idempotency keys for POST, ETag/Cache-Control/conditional requests.
  4. Pagination, filtering & sorting — cursor vs offset, bounded page sizes, consistent query params.
  5. AuthN/AuthZ — bearer tokens on every request (stateless), scopes/roles, transport security, rate limits (OWASP API Top 10).
  6. Versioning & evolution — additive changes, deprecation policy, URL vs header versioning.
  7. Errors & contracts — a consistent error envelope (e.g. RFC 9457 problem+json), correlation IDs, documented schema.
Design a RESTful API for a blogging platform (posts, comments, authors). Walk the resource model and maturity level.

A strong answer covers: resources as nouns — /posts, /posts/{id}, /posts/{id}/comments, /authors/{id} — nesting only where ownership is real, plus a flat /comments/{id} for direct access → verbs mapped cleanly (GET, POST, PATCH, DELETE) with correct status codes (201 + Location, 404 vs 410, 409 on conflict) → statelessness: bearer token per request, per-resource authorization (an author edits only their own posts) → caching with ETag/Last-Modified and conditional requests on writes → pagination + filtering on collections (cursor-based, bounded size) → a consistent error envelope and a versioning/deprecation story → name the maturity call: deliberately Level 2, and say when you'd move to L3.

You're handed a Level 0 "JSON over HTTP" API (one POST /api with an action field) and asked to make it RESTful. How do you approach it?

A strong answer covers: diagnose first — it's RPC tunnelled through one endpoint, so verbs, status codes, and caching carry zero meaning → climb the Richardson ladder incrementally, not big-bang: L1 split into resource URIs, L2 map each action to the correct method + status code and expose real cache headers → keep the old endpoint behind a versioned facade so existing clients don't break; run both during migration → tie every change to a benefit (cacheability, idempotent retries, intermediary visibility) over purity → stop at L2 unless uncontrollable third-party clients or a genuine state machine justify HATEOAS → call out migration risks: dual-write/dual-read consistency, client cutover, deprecation timeline with metrics.

Was this lesson helpful? Thanks — noted.

These notes reflect my current understanding and are updated as I learn, build, and discover better explanations.