REST API Introduction
REST principles, HTTP verbs mapped to CRUD, and a resource-oriented URL design example.
What REST actually means
REST (Representational State Transfer) is an architectural style for designing networked APIs, defined by Roy Fielding in his 2000 doctoral dissertation. It isn't a protocol or a standard you install — it's a set of constraints that, followed together, produce APIs that are predictable, cacheable, and easy to evolve. An API described as "RESTful" generally follows these core principles:
- Resources — everything the API exposes is modeled as a resource (a user, an order, a product), each identified by a URL. You don't call a "verb" like
/getUser; you address a "noun" like/users/42. - Uniform interface — a small, fixed set of operations (the standard HTTP methods) apply consistently across every resource, instead of each endpoint inventing its own action names.
- Statelessness — every request from a client must contain all the information the server needs to process it. The server stores no client session state between requests; each request is handled independently, using data (like an auth token) the client sends every time.
- Client-server separation — the client (UI) and server (data and business logic) evolve independently, connected only by the API contract between them.
- Representations — the client never gets the resource itself; it gets a representation of its current state, almost always as JSON.
Resources, not actions
The biggest shift REST asks for, compared to older RPC-style ("Remote Procedure Call") APIs, is thinking in nouns instead of verbs. Instead of designing an endpoint per action:
POST /getUser?id=42
POST /createUser
POST /deleteUser?id=42
POST /updateUserEmail
REST models the resource (user) once, and lets the HTTP method express the action:
GET /users/42 -> read a specific user
POST /users -> create a new user
PATCH /users/42 -> partially update a user
DELETE /users/42 -> delete a user
This is the "uniform interface" constraint in practice: once you know a URL identifies a resource, you already know the full menu of things you can do to it, without reading endpoint-specific documentation for every single action.
HTTP verbs mapped to CRUD
| HTTP method | CRUD operation | Typical use |
|---|---|---|
GET |
Read | Fetch a resource or a collection. Safe (no side effects) and cacheable. |
POST |
Create | Create a new resource inside a collection. Not idempotent — calling it twice creates two resources. |
PUT |
Update (replace) | Replace a resource entirely with the given representation. Idempotent — calling it twice has the same effect as calling it once. |
PATCH |
Update (partial) | Modify only the given fields of a resource. |
DELETE |
Delete | Remove a resource. Idempotent — deleting an already-deleted resource still results in it being gone. |
"Idempotent" is a term worth being precise about: it means making the same request multiple times produces the same end state as making it once — it does not mean the response is identical every time (a repeated DELETE on an already-deleted resource might return 404 the second time, but the resource's state — "gone" — hasn't changed).
Resource-oriented URL design — a worked example
Consider designing an API for a blogging platform: posts, each with comments, written by authors.
GET /authors/7/posts -> list posts by author 7
GET /posts/123 -> get a single post
POST /posts -> create a new post
PATCH /posts/123 -> update fields on post 123
DELETE /posts/123 -> delete post 123
GET /posts/123/comments -> list comments on post 123 (a nested resource)
POST /posts/123/comments -> add a comment to post 123
DELETE /posts/123/comments/45 -> delete comment 45 on post 123
Notice a few conventions that make this design predictable:
- Plural nouns for collections (
/posts, not/post). - Nesting expresses ownership/containment (
/posts/123/comments— comments that belong to post 123), but avoid nesting more than two levels deep —/authors/7/posts/123/comments/45/replies/9becomes painful to work with. Prefer/comments/45/repliesonce a resource has its own clear identity. - No verbs in the path.
/posts/123/publishis a common, pragmatic exception for actions that don't map cleanly onto CRUD (see below) — but the default should always be a plain resource path plus an HTTP method.
When an action doesn't fit CRUD
Not everything is naturally create/read/update/delete. "Publish this draft post" or "reset this user's password" are actions, not field updates. Two common, accepted patterns:
POST /posts/123/publish -> a sub-resource-like action endpoint (common, pragmatic)
PATCH /posts/123 -> { "status": "published" } (modeling it as a field change)
Both are used in real-world APIs; the second is more strictly "RESTful," but the first is often clearer intent and is a widely accepted pragmatic compromise.
Common mistakes
- Designing endpoints around actions (
/createOrder,/cancelOrder) instead of resources (POST /orders,PATCH /orders/9with a status change) — this is the most common way an API ends up "REST in name only." - Using
GETfor anything that changes server state —GETmust always be safe (no side effects), since browsers, proxies, and crawlers may call it without the user's explicit intent (e.g., link prefetching). - Treating
PUTandPATCHas interchangeable —PUTimplies replacing the entire resource;PATCHimplies a partial update. Sending a partial body to aPUTendpoint can unintentionally null out fields the client didn't include.
Interview questions
Q: What does it mean for an API to be "resource-oriented"?
It means the API is modeled around nouns (resources like /users, /orders) identified by URLs, with a small, consistent set of HTTP methods (GET, POST, PUT, PATCH, DELETE) expressing actions on those resources — rather than one bespoke endpoint per action, as in RPC-style APIs.
Q: Why is statelessness one of REST's core constraints? Because it means any server instance can handle any request — there's no session state pinned to a specific server that a load balancer needs to route around. This makes RESTful APIs easier to scale horizontally and easier to reason about, since each request is self-contained and doesn't depend on server memory of prior requests.