Engineering Notes
Home/Backend/API Design/REST Fundamentals
Backend · API Design

REST Fundamentals

REST is an architectural style, not a protocol. Learn the constraints that make an HTTP API RESTful, see them at work with load balancers, caches and hypermedia links, and grade APIs with the Richardson Maturity Model.

⚙️ Backend● Beginner⏱ 8 min read▶ 5 interactive🛠️ 3 exercises🎤 5 interview Q&As

🎯 What you'll learn

  • The six REST constraints and what each one buys you
  • Resources vs representations, and content negotiation
  • HATEOAS and the Richardson Maturity Model
  • How caching really works: Cache-Control, ETag and 304

1What REST Actually Is

REST (Representational State Transfer) is not a protocol, a library, or a file format — it is an architectural style defined by Roy Fielding in his 2000 doctoral dissertation. It describes a set of constraints that, when applied together to a client-server system built on HTTP, produce specific properties: scalability, a uniform interface, independent evolution of client and server, and cacheability.

"REST API" is shorthand for "an HTTP API that follows (most of) these constraints." Plenty of APIs are called "REST" while only loosely following the style — this topic covers what the style actually requires versus what has become colloquial usage.

💡 Example: PetPal's public API

Partners integrate with PetPal through endpoints like:

http
GET    /v1/pets/77              → read one pet
GET    /v1/owners/12/pets       → list an owner's pets
POST   /v1/orders               → create an order
DELETE /v1/orders/482           → cancel/delete an order

These are nouns (resources) acted on by the standard HTTP verbs. The rest of this lesson explains which constraints make this design REST and what each one buys you.

2The Six Constraints

#ConstraintWhat it means
1Client-serverUI concerns and data storage concerns are separated; either can evolve independently.
2StatelessEach request from client to server must contain all the information needed to understand and process it. The server holds no client session state between requests.
3CacheableResponses must implicitly or explicitly label themselves as cacheable or not, so clients (and intermediaries) can reuse response data.
4Uniform interfaceThe core defining trait of REST — see section 3.
5Layered systemA client can't tell whether it's connected directly to the origin server or to an intermediary (load balancer, cache, gateway) — layers can be added transparently.
6Code-on-demand (optional)Servers can temporarily extend client functionality by transferring executable code (e.g. client-side scripts). This is the only optional constraint.

3The Uniform Interface — Four Sub-Constraints

The uniform interface is what makes REST "REST" rather than just "an HTTP API." It has four parts:

  1. Identification of resources — every piece of information the API exposes (a user, an order, a collection of orders) is a resource, addressed by a stable URI (/orders/482).
  2. Manipulation of resources through representations — a client never touches the resource directly; it exchanges representations of it (a JSON document, typically) and the server translates between representation and internal state.
  3. Self-descriptive messages — each message includes enough information to describe how to process it: a Content-Type header telling the client/server how to parse the body, standard HTTP methods and status codes carrying well-known semantics.
  4. HATEOAS (Hypermedia As The Engine Of Application State) — responses include links describing what the client can do next, so clients aren't hard-coded with URI structure. This is the most-skipped constraint in real-world "REST" APIs.
ℹ️
Why 'uniform' matters

Because every resource is read with GET, created with POST, and so on, generic tools work with any REST API without custom code: browsers, CDNs, proxies, API gateways, monitoring and client libraries.

4Resource vs. Representation

A resource is the concept (the order with id 482). A representation is the serialized snapshot of that concept sent over the wire at a point in time.

http
GET /orders/482 HTTP/1.1
Host: api.example.com
Accept: application/json
http
HTTP/1.1 200 OK
Content-Type: application/json
ETag: "a1b2c3"

{
  "id": 482,
  "status": "shipped",
  "total": 129.99,
  "items": [
    { "sku": "SKU-100", "qty": 2 }
  ]
}

Same resource, different representation is entirely possible — the same order could be returned as XML, HAL+JSON, or a PDF invoice depending on Accept negotiation, without the underlying resource changing.

💡 Example: one resource, two representations
http
GET /orders/482
Accept: application/json      → { "id": 482, "status": "shipped", ... }

GET /orders/482
Accept: application/pdf       → %PDF-1.7 … (an invoice document)

The resource (order 482) is the same; the server picks the representation through content negotiation.

5HATEOAS in Practice

A HATEOAS-compliant response embeds the actions available on the current resource as links, rather than the client hard-coding URL patterns:

json
{
  "id": 482,
  "status": "shipped",
  "total": 129.99,
  "_links": {
    "self": { "href": "/orders/482" },
    "cancel": { "href": "/orders/482/cancel", "method": "POST" },
    "invoice": { "href": "/orders/482/invoice" }
  }
}

If status were "cancelled" instead, the cancel link would simply be absent — the client discovers valid transitions from the response itself instead of encoding business rules like "you can only cancel a shipped order" on the client side.

6The Richardson Maturity Model

Leonard Richardson's maturity model is a practical way to grade how "REST-ful" an API actually is, in four levels (0-3):

LevelNameCharacteristics
0The Swamp of POXSingle endpoint, everything is a POST, method name lives in the payload ({"action": "getOrder"}). This is RPC-over-HTTP, not REST.
1ResourcesMultiple endpoints, one per resource type (/orders, /users), but still mostly using POST for everything.
2HTTP VerbsResources and correct use of HTTP methods and status codes (GET to read, POST to create, DELETE to remove, proper 2xx/4xx/5xx). Most real-world "REST APIs" stop here.
3Hypermedia Controls (HATEOAS)Level 2 plus responses that embed the links/actions a client can take next. This is what Fielding considered actually REST.
Richardson Maturity Model — four levels from a single RPC-style endpoint up to full hypermedia controls
Richardson Maturity Model — four levels from a single RPC-style endpoint up to full hypermedia controls

7REST vs. RPC vs. SOAP (Quick Contrast)

RESTRPC (e.g. gRPC)SOAP
Core unitResources (nouns)Procedures/functions (verbs)Operations (verbs)
Payload formatUsually JSONUsually Protocol Buffers (binary)XML (strict envelope)
Transport couplingHTTP semantics (methods, status codes) are part of the contractTransport-agnostic, HTTP/2 in gRPC's case, used only as a carrierHTTP is just a carrier; the envelope carries everything
ContractOften OpenAPI (descriptive, optional).proto file (mandatory, code-generated)WSDL (mandatory, strict)
Typical usePublic/partner-facing web APIsInternal service-to-service callsLegacy enterprise/financial systems
Under the HoodHow it works internally▾

Statelessness is enforced by what the protocol doesn't give you

HTTP itself has no built-in concept of a "session." A stateless REST server literally cannot rely on remembering anything about a specific TCP connection or a prior request from that client — each request is parsed, authenticated (usually via a bearer token or API key sent in that very request's headers), processed, and the connection can be dropped or reused for a totally different client on the next request thanks to connection pooling/keep-alive. This is precisely why "session cookies that the server looks up in server memory" are not RESTful — they reintroduce server-held client state. A stateless design instead puts everything needed to process the request — auth token, pagination cursor, filters — into the request itself (headers, query string, or body).

Cacheability is implemented via real HTTP headers, not a REST-specific mechanism

The "cacheable" constraint isn't abstract — it's implemented by two concrete HTTP mechanisms:

  • Cache-Control — declares freshness rules directly (Cache-Control: max-age=60, public tells any cache, including browsers and CDNs, this response can be reused for 60 seconds without re-asking the server).
  • ETag + conditional requests — the server computes an opaque fingerprint (ETag: "a1b2c3", typically a hash of the representation) for a resource. A client re-requesting the same resource sends If-None-Match: "a1b2c3". If the resource hasn't changed, the server can skip re-sending the body entirely and reply 304 Not Modified, saving bandwidth while still confirming the client's cached copy is valid.
http
GET /orders/482 HTTP/1.1
If-None-Match: "a1b2c3"

HTTP/1.1 304 Not Modified

HATEOAS is a media-type problem, not a JSON-structure problem

Plain application/json has no standardized concept of a "link." That's why real HATEOAS implementations use hypermedia-aware media types — application/hal+json (defines _links formally), application/vnd.api+json (JSON:API spec), or application/problem+json for errors (its own topic). The _links object shown in section 5 is really the HAL convention; without an agreed media type, "links in the JSON body" is just an informal convention the client has to know about in advance, which weakens the whole point of hypermedia (letting the client discover behavior instead of hard-coding it).

⚠️ Common Pitfalls

  1. Calling any JSON-over-HTTP API "REST" — an API with a single /api endpoint that dispatches on an action field in the body (Richardson level 0) is RPC, not REST, regardless of what it's marketed as.
  2. Confusing "stateless" with "no persisted data" — statelessness is about the server not holding session/conversation state between requests, not about whether the server has a database. The order itself (persisted state) is completely fine; a server-side "current step in a multi-request wizard" is not.
  3. Skipping HATEOAS and calling it a REST API anyway — this is extremely common (most "REST APIs" are actually Richardson Level 2), and it's a reasonable pragmatic choice, but it's worth knowing you've made that trade-off rather than believing you've implemented "true REST."
  4. Treating the URI as an implementation detail the client should parse — e.g. clients constructing /orders/${id}/cancel themselves by string concatenation instead of following a cancel link from the resource — reintroduces tight coupling REST was designed to avoid.
  5. Assuming REST mandates JSON — REST is representation-agnostic; JSON just won because it's convenient, not because the style requires it.
RPC dressed up as REST
http
POST /api
{ "action": "cancelOrder", "id": 482 }

200 OK
{ "ok": false, "error": "not found" }
Resource + verb + status code
http
POST /orders/482/cancel     (a link from the order)

404 Not Found
Content-Type: application/problem+json
{ "title": "Order not found" }

📋 Quick Reference

text
6 constraints: client-server, stateless, cacheable, uniform interface, layered system, code-on-demand (optional)
Uniform interface: resource identification, representations, self-descriptive messages, HATEOAS
Richardson Maturity Model: 0 (RPC-over-HTTP) -> 1 (resources) -> 2 (HTTP verbs) -> 3 (hypermedia/HATEOAS)
Cacheability mechanics: Cache-Control (freshness) + ETag/If-None-Match (validation, 304 Not Modified)
Most "REST APIs" in the wild = Richardson Level 2 (resources + verbs, no HATEOAS)

🧠 Key takeaways

  • REST is a style: client-server, stateless, cacheable, uniform interface, layered system, and optional code-on-demand.
  • Statelessness lets any server handle any request, which is what makes horizontal scaling and restarts painless.
  • Cacheability is real HTTP: Cache-Control for freshness, ETag/If-None-Match for validation (304).
  • Most APIs are Richardson Level 2; HATEOAS (Level 3) moves the rules about valid actions onto the server, discovered through links.

🎤 Interview Q&A

Questions interviewers ask about this topic. Answer out loud first, then open a card to compare.

Q1What makes an API RESTful?core▾

REST constraints: client-server, statelessness, cacheability, a uniform interface (resources identified by URIs, manipulated through representations, standard methods, self-descriptive messages, hypermedia), layered system, and optional code-on-demand. In practice, 'REST' usually means resource-oriented HTTP APIs using methods and status codes correctly.

Follow-ups interviewers ask
  • What's HATEOAS? Responses include links to related actions/resources, letting clients navigate without hard-coded URLs.
Q2What is the Richardson Maturity Model?core▾

Level 0: one endpoint, RPC over HTTP. Level 1: resources. Level 2: HTTP methods and status codes used properly. Level 3: hypermedia controls. Most production 'REST' APIs are level 2.

Follow-ups interviewers ask
  • Is level 3 necessary? Rarely in practice; level 2 with good docs is the norm.
Q3REST vs RPC style APIs?core▾

REST models nouns (resources) manipulated by standard verbs; RPC models actions (/sendInvoice). REST gets caching, uniformity and tooling; RPC (gRPC) fits internal, action-heavy or high-performance calls. Many REST APIs use RPC-style sub-resources for actions (POST /orders/42/cancel).

Follow-ups interviewers ask
  • Is POST /orders/42/cancel acceptable? Yes, it's a pragmatic way to model state transitions.
Q4Why does statelessness matter?senior▾

Each request carries all context (auth token, parameters), so any server can handle it: easy horizontal scaling, load balancing and failover. Session state, if needed, lives in shared stores.

Follow-ups interviewers ask
  • Are cookies stateless? The cookie is sent with each request; the server-side session it references is state.
Q5What makes a REST API easy to use?core▾

Consistent naming and structure, predictable status codes and error format, good docs (OpenAPI) with examples, pagination and filtering conventions, versioning strategy, idempotency for retries, and sensible defaults.

Follow-ups interviewers ask
  • Biggest usability killer? Inconsistency across endpoints.

🛠️ Practice exercises

Each exercise is a file in this topic folder. Code exercises are self-checking: run them and make the checks pass.

✅ Check yourself

Answer the questions — feedback is instant.

1. What is REST?
Fielding's dissertation defines REST as constraints that produce properties such as scalability and evolvability.
2. Which design violates statelessness?
Persisted resources are fine; session or conversation state held on the server between requests is not.
3. An API uses resources and correct HTTP verbs/status codes but no links. What Richardson level is it?
Level 3 adds hypermedia controls (HATEOAS); most APIs stop at Level 2.
4. A client sends If-None-Match: "a1b2c3" and the resource is unchanged. The server should reply…
ETag validation lets the client reuse its cached copy with a tiny 304 response.
5. In a HATEOAS response for a cancelled order, the cancel link is…
Links describe currently valid transitions, so clients don't hard-code business rules.