REST — Representational State Transfer
REST is an architectural style for APIs that uses HTTP verbs on resources identified by URLs. It is stateless, cacheable, and uniform. Most public web APIs use REST (or something close to it) because it is simple, widely understood, and works over standard HTTP.
How it works
REST (Representational State Transfer) is not a protocol — it's an architectural style defined by Roy Fielding in 2000. It constrains how you design APIs over HTTP so they are scalable, simple, and cacheable.
The core idea: treat everything as a resource identified by a URL, and use HTTP verbs (GET, POST, PUT, DELETE) to act on those resources.
REST has 6 constraints (Fielding's original definition):
- Client-server: separation of concerns. UI (client) and data (server) evolve independently.
- Stateless: each request contains all info needed. No server-side session. Enables scaling.
- Cacheable: responses declare if they're cacheable (Cache-Control, ETag). Improves performance.
- Uniform interface: resources identified by URLs, manipulated via representations (JSON/XML), self-descriptive messages, HATEOAS.
- Layered: client doesn't know if it's talking to the server directly or through a proxy/CDN.
- Code-on-demand (optional): server can send executable code (JavaScript) to the client.
Most 'REST APIs' in practice only follow the first 3-4 constraints. True HATEOAS (Hypermedia As The Engine Of Application State) is rare.
REST thinks in terms of resources and verbs: DELETE /users/42. RPC thinks in terms of actions: POST /deleteUser {id: 42}. REST is more uniform and cacheable; RPC is more natural for complex operations. gRPC (later lesson) is a modern, high-performance RPC framework.
Good REST API design:
- Plural nouns:
/usersnot/user./users/42/postsnot/getUserPosts. - Versioning:
/v1/usersorAccept: application/vnd.api+json;version=1. Never break existing clients. - Pagination:
GET /users?page=2&limit=20or cursor-basedGET /users?after=abc123. - Status codes: 200 OK, 201 Created, 204 No Content, 400 Bad Request, 404 Not Found, 429 Too Many Requests, 500 Internal Server Error.
- Filtering:
GET /users?role=admin&active=true— query params for filtering, not separate endpoints. - Consistent error format:
{"error": {"code": "NOT_FOUND", "message": "User 42 not found"}}
Which URL design is most RESTful?
Pick one answer.
Your REST API returns a list of 10,000 users. How should you handle this?
Pick one answer.
Engineering mental model
Mental model. Think of REST — Representational State Transfer as a deliberate boundary in a system. The boundary exists because something becomes harder to manage when everything is done in one place: latency, scale, failure isolation, consistency, cost, or team ownership. The useful question is not “what does REST — Representational State Transfer mean?” but “what pressure makes this boundary worth introducing, and what new failure mode does it create?”
Before choosing REST — Representational State Transfer, name the workload, the critical user path, the dominant bottleneck, the failure you are trying to absorb, and the trade-off you are willing to accept. If you cannot name those five things, the design is probably premature.
curl -i https://api.example.com/v1/rest
# Look for:
# - status code
# - latency
# - retryability
# - response sizeBack-of-the-envelope reasoning
Numerical lens: write down traffic, payload size, read/write ratio, peak multiplier and durability target before choosing a component. The numbers should justify the architecture.
Interactive thought experiment: REST — Representational State Transfer
Change the variables below and predict what breaks first in REST — Representational State Transfer. The production lab can later reuse these same inputs.
Change one variable at a time. Predict the failure mode first, then move the slider and see whether your mental model matches the simplified system response.
If you are stuck on REST — Representational State Transfer, start by drawing the request path and marking every network hop, stateful component, queue, cache and failure boundary. Then estimate where the system will saturate.
You increase traffic by 10× in a system using REST — Representational State Transfer. What should you inspect first?
Pick one answer.
Which statement is the safest engineering habit when using REST — Representational State Transfer?
Pick one answer.
You have dashboards for traffic, latency, errors and saturation. You can change the architecture, but every change has operational cost.
Production scenario: your system uses REST — Representational State Transfer, traffic suddenly spikes, and p99 latency doubles. What is your first move?
Interview drill
Answer this without notes: When would you choose REST — Representational State Transfer, and when would you intentionally avoid it? Mention at least one bottleneck it addresses, one failure mode it introduces, and one alternative. Then quantify the workload you are designing for.
A useful engineering lens for REST - Representational State Transfer: define the problem it solves, the simpler design that fails first, the constraint that forces you to introduce this concept, and the new failure modes the concept creates.
Numerical sanity check
Back-of-the-envelope reasoning beats fake precision. State your traffic, payload, concurrency and growth assumptions explicitly, then calculate enough to know whether the current architecture is orders of magnitude away from the target.
Do not optimize for a memorized definition. Reason from the workload and failure mode.
Imagine the simplest version of a system using REST - Representational State Transfer. What breaks first as traffic grows by 10×, and what would you change before reaching 100×?
Pick one answer.
What you gain, what you pay
- +Simple and widely understood — every developer knows HTTP.
- +Cacheable — HTTP caching (ETag, Cache-Control) works out of the box.
- +Stateless — easy to scale (any server handles any request).
- +Browser-friendly — works without special client libraries.
- −Over-fetching or under-fetching — the client gets the whole resource or must make multiple requests. (GraphQL solves this.)
- −Text-based (JSON) — less efficient than binary protocols (gRPC/Protobufs).
- −Request-response only — not suitable for streaming or server push (use WebSockets/SSE).
- −Multiple round trips — fetching related resources requires multiple requests.
How this breaks in production
- Returning 200 OK with an error body — breaks HTTP semantics and client error handling.
- Using GET for operations with side effects — breaks caching and violates HTTP.
- No pagination — large responses crash clients and servers.
- Breaking changes without versioning — existing clients stop working.
Don't fall into these traps
- •Embedding verbs in URLs (e.g., /getUser) — use HTTP verbs instead.
- •Forgetting idempotency — POST is not idempotent, so retries can duplicate resources.
- •Returning huge nested objects — use pagination and expansion (?expand=posts).
Real systems using this
How real systems implement this
- GitHub API — Textbook REST: plural nouns, HTTP verbs, pagination via Link headers, versioning via URL (/v3/), HATEOAS links in responses.
- Stripe API — REST with idempotency keys and expandable resources (?expand[]=customer) to avoid over-fetching.
Practice saying it out loud
- Q1What are the REST constraints? Which ones do most 'REST APIs' actually follow?
- Q2REST vs GraphQL vs gRPC — when would you choose each?
- Q3How do you version a REST API?
Further reading & references
Core explanations are original NO CAP material. External references are provided for deeper study and standards.
What next?
Mark as understood once the mental model clicks.
Next recommended
GraphQL — Query Language for APIs