REST
In a Nutshell
REST (Representational State Transfer) is an architectural style for designing networked APIs around resources — nouns like users, orders, products — that you manipulate using standard HTTP methods. Instead of calling arbitrary functions, you GET a resource to read it, POST to create one, PUT/PATCH to update, and DELETE to remove it. REST's genius is that it leans entirely on the existing machinery of HTTP: URLs identify resources, methods define actions, status codes report outcomes, and caching/proxies work for free. This simplicity and universality made REST the default style for web APIs — it's the baseline every backend engineer must know cold.

How It Actually Works
Resources and Methods: The Core Mapping
REST models everything as resources addressed by URLs, acted on by HTTP methods (see HTTP & HTTPS):
| Method | Action | Example | Idempotent? |
|---|---|---|---|
| GET | Read | GET /orders/42 |
Yes |
| POST | Create | POST /orders |
No |
| PUT | Replace (full) | PUT /orders/42 |
Yes |
| PATCH | Update (partial) | PATCH /orders/42 |
No (usually) |
| DELETE | Remove | DELETE /orders/42 |
Yes |
Resource-oriented URLs (nouns, not verbs):
✅ GET /users/42/orders (get user 42's orders)
✅ POST /users/42/orders (create an order for user 42)
❌ GET /getUserOrders?id=42 (verb in URL — not RESTful)
❌ POST /createOrder (RPC-style, not resource-oriented)
The REST Design Principles
| Principle | Meaning |
|---|---|
| Client-server | Separation of concerns; they evolve independently |
| Stateless | Each request contains everything needed; server keeps no session |
| Cacheable | Responses declare cacheability (leverage HTTP caching) |
| Uniform interface | Consistent resource URLs + standard methods + status codes |
| Layered system | Proxies, gateways, LBs can sit between transparently |
Statelessness is the most consequential: because the server holds no per-client state, any server can handle any request — the foundation of horizontal scaling (see Vertical vs Horizontal Scaling).
Status Codes: Communicating Outcomes
Using the right status code is part of good REST design — clients and proxies rely on them:
2xx success: 200 OK, 201 Created, 204 No Content
3xx redirect: 301 Moved, 304 Not Modified (caching)
4xx client: 400 Bad Request, 401 Unauthorized, 403 Forbidden,
404 Not Found, 409 Conflict, 422 Unprocessable, 429 Too Many
5xx server: 500 Internal Error, 502 Bad Gateway, 503 Unavailable
Richardson Maturity Model
A useful ladder for "how RESTful" an API is:
| Level | Description |
|---|---|
| 0 | HTTP as a tunnel for RPC (one endpoint, POST everything) |
| 1 | Resources (distinct URLs per resource) |
| 2 | HTTP verbs + status codes used properly (most "REST" APIs live here) |
| 3 | HATEOAS — responses include links to related actions (rarely fully adopted) |
Most production "REST" APIs are Level 2 — resource URLs plus correct methods and status codes — and that's usually the pragmatic target.
Good REST Design Conventions
- Nouns, plural, hierarchical:
/users/42/orders/7, not verbs. - Filtering/sorting/pagination via query params:
/orders?status=shipped&sort=-created&page=2. - Versioning:
/v1/ordersor a header (see API Versioning & Idempotency). - Consistent errors: a structured error body (
{ "error": { "code": "...", "message": "..." } }). - Use standard status codes rather than always returning 200 with an error flag.
REST's Limitations
REST isn't perfect, which is why GraphQL and gRPC exist:
- Over-fetching — an endpoint returns more data than the client needs.
- Under-fetching / N+1 — the client must make many calls to assemble a view (fetch user, then each of their orders, then each order's items).
- Rigid responses — the server decides the shape; clients can't ask for exactly what they need. (This is precisely what GraphQL addresses.)

Seeing It in Action
Scenario: A well-designed REST API for an orders resource.
# List with filtering, sorting, pagination
GET /v1/users/42/orders?status=shipped&sort=-created_at&page=2&limit=20
→ 200 OK
{ "data": [ {...}, {...} ],
"pagination": { "page": 2, "limit": 20, "total": 137,
"next": "/v1/users/42/orders?...&page=3" } }
# Create — returns 201 with a Location header
POST /v1/users/42/orders
{ "items": [ { "product_id": 9, "qty": 2 } ] }
→ 201 Created
Location: /v1/users/42/orders/7
{ "id": 7, "status": "pending", ... }
# Partial update
PATCH /v1/orders/7
{ "status": "cancelled" }
→ 200 OK { "id": 7, "status": "cancelled", ... }
# Conditional GET leverages HTTP caching
GET /v1/orders/7
If-None-Match: "v3-abc"
→ 304 Not Modified (nothing re-sent; cache still valid)
# Errors use proper status codes + structured bodies
POST /v1/users/42/orders (empty items)
→ 422 Unprocessable Entity
{ "error": { "code": "empty_order", "message": "Order must have items" } }
Why these conventions matter: the API is predictable — anyone who knows REST can guess that GET /v1/orders/7 reads order 7 and DELETE removes it, without reading docs. It's cache-friendly (ETags + 304 avoid redundant transfers), scalable (stateless — any server serves any request), and tooling-friendly (proxies, gateways, and HTTP clients all understand the methods and status codes for free). This is REST's enduring value: by conforming to HTTP's existing semantics, you inherit an entire ecosystem of caching, routing, and tooling with zero extra work.
Interview Questions
Q: What makes an API RESTful, and what does statelessness buy you? Hint: Resources addressed by URLs (nouns), manipulated via standard HTTP methods (GET/POST/PUT/PATCH/DELETE) with proper status codes, plus a uniform interface, cacheability, and a layered system. Statelessness — each request is self-contained and the server keeps no session — means any server can handle any request, enabling horizontal scaling, easy load balancing, and resilience (no lost sessions on server failure).
Q: How do you design resource-oriented URLs? Give good and bad examples. Hint: Use plural nouns and hierarchy, not verbs:
GET /users/42/orders(good) vsGET /getUserOrders?id=42orPOST /createOrder(bad — verbs/RPC style). Actions come from HTTP methods, not the URL. Filtering/sorting/pagination go in query params (/orders?status=shipped&page=2). The URL identifies the resource; the method defines the action.Q: What are over-fetching and under-fetching, and how do they motivate alternatives to REST? Hint: Over-fetching: an endpoint returns more fields than the client needs (wasted bandwidth). Under-fetching / N+1: the client must make many calls to assemble one view (fetch user → each order → each item). Both stem from the server dictating fixed response shapes. GraphQL addresses them by letting clients request exactly the fields and nesting they need in one query.
Q: What is the Richardson Maturity Model, and where do most REST APIs sit? Hint: A ladder: Level 0 (HTTP as an RPC tunnel), Level 1 (distinct resource URLs), Level 2 (proper HTTP verbs + status codes), Level 3 (HATEOAS — hypermedia links to related actions). Most production "REST" APIs are Level 2 — resource URLs with correct methods and status codes — which is the pragmatic sweet spot; full HATEOAS is rarely adopted.
Q: Why does using correct HTTP status codes and methods matter beyond aesthetics? Hint: Clients, proxies, caches, and gateways rely on them. Idempotent methods (GET/PUT/DELETE) can be safely retried; cacheable GETs with 304 avoid redundant transfers; 4xx vs 5xx tells clients whether to fix the request or retry; 429 + Retry-After enables intelligent backoff. Conforming to HTTP semantics means you inherit the entire ecosystem of caching, retry, and routing behavior for free.
References
- Roy Fielding's dissertation (REST) — the original definition
- Microsoft REST API Guidelines — practical, comprehensive conventions
- MDN HTTP methods — method semantics
Dive Deeper
- Richardson Maturity Model (Martin Fowler) — the levels explained
- Stripe API reference — a gold-standard REST API design
- REST API Design Rulebook by Mark Massé — deep conventions