HTTP, REST and the life of a request
Every conversation between the two halves is an HTTP request and a response. You have used HTTP for years without reading one. This lesson makes it concrete, because API design is mostly HTTP design.
A request
GET /api/products?category=grains&page=2 HTTP/1.1
Host: api.kirana.example
Accept: application/json
Authorization: Bearer eyJhbGci...
Four parts:
Method — GET. What kind of operation.
Path — /api/products. Which resource.
Query string — ?category=grains&page=2. Parameters.
Headers — metadata: what format is wanted, who is asking.
A POST, PUT or PATCH also carries a body:
POST /api/orders HTTP/1.1
Content-Type: application/json
{"items": [{"productId": "atta-5kg", "quantity": 2}]}
That is the whole shape. Everything else is detail.
A response
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: public, max-age=60
{"data": [...], "page": 2, "total": 47}
A status code, headers, and usually a body.
The methods
| Method | Means | Safe | Idempotent |
|---|---|---|---|
GET |
read something | yes | yes |
POST |
create, or "do something" | no | no |
PUT |
replace entirely | no | yes |
PATCH |
update partially | no | no |
DELETE |
remove | no | yes |
Two words worth defining, because they drive real decisions:
Safe means it changes nothing. A GET must never alter data — browsers,
proxies and crawlers all assume this, and a GET /products/5/delete will
eventually be triggered by something that was only trying to look.
Idempotent means doing it twice has the same effect as once. DELETE on an
already-deleted item changes nothing further.
POST is not idempotent, which is exactly why double-clicking a submit
button creates two orders. Module 12 handles that properly; for now, notice the
property is what causes the bug.
Status codes
The first digit tells you the category:
- 2xx — it worked
- 3xx — look elsewhere
- 4xx — the client got it wrong
- 5xx — the server got it wrong
The ones you will actually use:
| Code | When |
|---|---|
200 OK |
a successful read or update |
201 Created |
a successful create |
204 No Content |
success, nothing to return — often DELETE |
400 Bad Request |
malformed or invalid input |
401 Unauthorized |
not logged in |
403 Forbidden |
logged in, not allowed |
404 Not Found |
no such resource |
409 Conflict |
clashes with current state — out of stock |
422 Unprocessable |
well-formed but semantically wrong |
429 Too Many Requests |
rate limited |
500 Internal Server Error |
you have a bug |
401 versus 403 catches everyone. 401 means "I do not know who you are" — logging in might fix it. 403 means "I know exactly who you are and no" — logging in again will not help.
4xx is your fault, 5xx is mine. If a user sends bad data and you return 500, you are telling them your server broke when it did not — and you will get support tickets for it. Getting this split right is most of what makes an API pleasant to consume.
REST, briefly
REST is a set of conventions for mapping URLs and methods onto resources. It is not a specification you can fail, and most "REST APIs" are approximate. What matters is being predictable.
The convention:
GET /products list
POST /products create
GET /products/42 read one
PATCH /products/42 update
DELETE /products/42 delete
GET /products/42/reviews nested resource
The principles worth keeping:
Nouns in paths, verbs as methods. /products, not /getProducts. The
method already says what you are doing, so POST /createProduct says it twice.
Plural, consistently. /products/42, never /product/42. Pick one and
never mix.
Nest only one level. /products/42/reviews is fine.
/categories/3/products/42/reviews/7 is not — use /reviews/7.
Filtering and sorting in the query string, not the path:
GET /products?category=grains&sort=price&order=asc&page=2&limit=20
When REST does not fit
Some operations are not CRUD. Cancelling an order is not "update order".
POST /orders/42/cancel
POST /cart/checkout
POST /auth/login
These are verbs, and that is fine. Forcing PATCH /orders/42 with
{"status": "cancelled"} looks more RESTful and is worse — it implies any
status transition is allowed, when cancelling has its own rules.
Be consistent rather than pure. An API somebody can predict beats one that satisfies a specification.
Headers that matter
Content-Type: application/json — what you are sending.
Accept: application/json — what you want back.
Authorization: Bearer <token> — who you are. Module 8.
Cache-Control — how long this may be reused. Significant for a shop, where
product pages change rarely and carts change constantly.
Set-Cookie — the server storing something in the browser. Module 8 again.
Seeing it yourself
curl -i https://api.github.com/repos/facebook/react
-i includes the response headers. Read the status line, the content type, the
caching headers.
curl -X POST https://httpbin.org/post \
-H "Content-Type: application/json" \
-d '{"hello": "world"}'
httpbin.org echoes back what it received, which makes it a good place to see
exactly what your request looked like.
Your browser's Network tab shows the same for every request a page makes. Spend ten minutes in there on a site you use — it is the fastest way to make this concrete.
What this means for design
Every endpoint you write in module 7 is a decision about method, path, status code and body shape. Get those right and a front-end developer can guess your API. Get them wrong and they need documentation for every call.
The test: could somebody predict the URL and method for an operation they have not seen? If yes, the design is working.
Check your work
Methods and paths: list all products GET /products · get product 42
GET /products/42 · add a review POST /products/42/reviews · delete review 7
DELETE /reviews/7 · search GET /products?q=atta · page 3 of grains
GET /products?category=grains&page=3 · cancel order 12
POST /orders/12/cancel.
Status codes: successful login 200 · wrong password 401 · valid token but not an admin 403 · product does not exist 404 · out of stock at checkout 409 · database down 500.
Why GET /products/5/delete is a bad idea: GET must be safe. Crawlers,
prefetchers and proxies follow GET links freely, so something will eventually
delete your products just by looking.
Why a double-clicked button creates two orders: POST is not idempotent, so
performing it twice has twice the effect.
What a cart request looks like: a PATCH or POST to a cart endpoint,
sending an item id and a quantity — not a price — and returning 200.
Practice
curl -ia public API. Identify the status line, three headers and the body.- Use httpbin to send a POST with a JSON body. Read back what arrived.
- Write the method and path for: list all products · get product 42 · add a review to product 42 · delete review 7 · search for "atta" · get page 3 of grains · cancel order 12.
- For each, pick a status code: successful login · wrong password · valid token but not an admin · product does not exist · out of stock at checkout · your database is down.
- Explain in one sentence why
GET /products/5/deleteis a bad idea even though it works. - Explain why a double-clicked submit button creates two orders, using the word idempotent.
- Open a shop's Network tab, add something to the cart, and write down the method, path, request body and status code.
Next: setting up the project you will build for the rest of the course.
Stuck on this lesson?
Being stuck is part of it — but being stuck alone for three days is not. Our internship programme pairs this curriculum with code review and one-to-one help from working developers, and it is free.
About the internship