REST architecture.
Most APIs that call themselves RESTful aren't. The term has been diluted into 'JSON over HTTP.' Today we restore the precise meaning, and — more importantly — separate the rules worth following from the academic ones nobody actually does.
Where REST came from
In 2000, a researcher named Roy Fielding wrote a doctoral dissertation that retroactively described the architecture of the web — why HTTP scaled when other protocols didn't. He gave it a name: REST (Representational State Transfer).
So REST is not a protocol. It's not a format. It's a set of architectural constraints. If your API satisfies them, it's RESTful. If not, it's just an API. (Both are fine — but pretending matters.)
The six constraints
| Constraint | What it means |
|---|---|
| 1. Client-Server | Separate concerns: client handles UI, server handles data. They evolve independently. |
| 2. Stateless | Each request contains everything needed to process it. The server doesn't remember the client. |
| 3. Cacheable | Responses must declare whether they can be cached and for how long. |
| 4. Layered system | The client doesn't know if it's talking to the actual server or a load balancer / proxy / CDN. |
| 5. Uniform interface | One consistent way to identify, read, and modify resources (URIs + standard methods). |
| 6. Code on demand (optional) | Server can ship JavaScript to extend the client. Rarely listed as defining REST. |
The first five matter. The sixth is a footnote — Fielding even marked it optional.
The mental model · resources
The single most useful insight from REST: your API is a collection of resources, not a collection of actions.
❌ RPC-style — verbs in URLs
POST /getUserPOST /createUserPOST /updateUserEmailPOST /deleteUserPOST /listUsers
You'll explode in endpoint count. Each new operation is a new URL.
✅ REST — resources + standard methods
GET /users— listPOST /users— createGET /users/42— fetch onePUT /users/42— replacePATCH /users/42— partial updateDELETE /users/42— remove
Five operations, one URL pattern. Predictable.
URIs are nouns. The HTTP method is the verb. GET /users is correct. GET /getUsers is REST cosplay. Always plural for collections (/users), singular sub-resources via id (/users/42/orders).
Nesting resources
Sub-resources express ownership:
GET /users/42/orders # orders for user 42
POST /users/42/orders # create an order for user 42
GET /users/42/orders/9 # specific order
DELETE /users/42/orders/9 # remove itTwo informal rules in practice:
- Don't nest more than 2 levels.
/users/42/orders/9/items/3is too deep — make/items/3a top-level resource. - Filter via query params, not paths.
GET /orders?userId=42&status=paid&sort=createdAtover deep paths.
Statelessness — the constraint that scales
"Each request contains everything to process it" sounds abstract. Concretely:
❌ Stateful
POST /login
→ server stores session
→ returns session-id
GET /me
→ server looks up session
→ from in-memory mapServer holds state. Hard to scale to many machines.
✅ Stateless
POST /login
→ returns signed JWT
GET /me
Authorization: Bearer eyJ...
→ verify token signature
→ all info in the tokenAny server can handle any request. Trivial to scale.
Statelessness lets you put a load balancer in front of N replicas of your service. No sticky sessions, no shared session store. Add machines for traffic spikes; remove them when quiet. This is why JWT-based auth dominates modern REST APIs.
Cacheable — leverage the network
HTTP caching is built into the protocol. Use it:
GET /products/42 HTTP/1.1\n\nHTTP/1.1 200 OK\nCache-Control: public, max-age=3600\nETag: "abc123"The Cache-Control: max-age=3600 tells browsers, CDNs, proxies — "you can cache this for an hour." A subsequent request can be answered from cache without ever hitting your server.
ETag + If-None-Match lets clients ask "is the version I have still valid?" — server responds 304 Not Modified with no body if yes.
The Richardson Maturity Model
Leonard Richardson described how "RESTful" a given API is on a four-step ladder. Most "REST" APIs are at level 2 — that's fine.
HATEOAS — the academic constraint nobody follows
Level 3 says responses should include links the client can follow:
{
"id": 42,
"status": "paid",
"_links": {
"self": { "href": "/orders/42" },
"refund": { "href": "/orders/42/refund" },
"invoice": { "href": "/orders/42/invoice" }
}
}Idea: clients discover capabilities at runtime, not by reading docs. In practice, almost no public API does this. Clients are written against API docs, not by exploring links. Spring HATEOAS exists; it's mostly used in enterprise systems with strict requirements. Don't worry about HATEOAS for interviews — recognize the term, move on.
How Spring expresses REST
@RestController
@RequestMapping("/users")
public class UserController {
private final UserService userService;
@GetMapping
public List<UserDto> list() { return userService.findAll(); }
@GetMapping("/{id}")
public UserDto get(@PathVariable Long id) { return userService.findById(id); }
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public UserDto create(@Valid @RequestBody CreateUserRequest req) { return userService.create(req); }
@PatchMapping("/{id}")
public UserDto update(@PathVariable Long id, @RequestBody PatchUserRequest req) { return userService.patch(id, req); }
@DeleteMapping("/{id}")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void delete(@PathVariable Long id) { userService.delete(id); }
}Every line maps cleanly to REST conventions: @GetMapping, plural collection in path, @PathVariable for the resource id, 201 Created on POST, 204 No Content on DELETE. Spring makes the right thing easy.
The five most common REST mistakes
| Mistake | Right way |
|---|---|
GET /getAllUsers | GET /users |
POST /users/42/delete | DELETE /users/42 |
Returning 200 OK with { "error": ... } | Return 4xx / 5xx with error body |
Same URL for HTML and JSON via ?format=json | Content negotiation via Accept header |
Nesting beyond 2 levels (/a/1/b/2/c/3) | Promote inner resources to top level |
API versioning — the unavoidable conversation
You'll change your API. Clients depend on it. How do you make changes without breaking them?
URL-based · most common
/v1/users·/v2/users- Easy to route, easy to test
- "Clutters" URLs but everyone accepts it
- Stripe, Twilio, GitHub use this
Header-based · cleaner but fragile
Accept: application/vnd.api.v2+json- URLs stay clean
- Hard to test in browsers / curl
- Caching gets weird
- Few public APIs use this in practice
Use URL versioning for public APIs. Treat each version as a contract — once published, never break it. New version when you must break. Within a version, make additive changes (new optional fields are safe; renaming fields is not).
REST vs. GraphQL vs. gRPC — the lay of the land
| Style | Best for | Tradeoff |
|---|---|---|
| REST | Public APIs, broad client compatibility | Multiple round-trips for related data |
| GraphQL | Complex frontends fetching many related objects | Server complexity; harder to cache |
| gRPC | Internal microservice ↔ microservice calls | Binary; harder to debug; not browser-native |
For your career trajectory: master REST. It's the lingua franca. The other two come up only when a specific need arises.
Lock in today's learning
Take your time. These come up in real design discussions.
- What is REST in one sentence? (Hint: not a protocol)
- Name three of Fielding's six constraints and why they matter.
- What's the resource-based naming rule? Give a wrong example and a right one.
- Why does statelessness make scaling easier?
- What is the Richardson Maturity Model and where do most production APIs live?
- Name three common REST mistakes you'll watch for in code review.
End of Day 13. Tomorrow: JSP — a brief historical tour, the bridge to today's frontend separation.