System Design · Module 2 · Lesson 2 of 4 · 40 min
HTTP Methods Status Codes and Resource Contracts
Design a small resource API with clear ownership, validation, success responses and failures a caller can handle.
01 The idea
The problem this lesson solves
POST /v1/notes sends a JSON note body; the server derives its owner.
Prerequisite: earlier lessons introduce the vocabulary and shared examples used here. Follow the module order.
02 Worked example
Follow one worked example
Trace the example in order. Follow the same identities, units and state transitions through the diagram and the events below.
A resource contract defines the operation, caller permission, valid input and every meaningful outcome.
03 The method
Mechanisms decisions and a guided tutorial
Work through the explanation in order, then apply it in the tutorial. Each comparison names a cost as well as a benefit.
| Choice or concept | Purpose | Boundary or cost |
|---|---|---|
| GET | Read a resource | Safe and idempotent semantics |
| POST | Create or invoke | Needs an explicit retry contract |
| PUT | Replace at a known resource | Idempotent intended effect |
| DELETE | Remove a resource | Repeated status codes may differ |
Make resources and ownership explicit
Use POST /v1/links to create a link, GET /v1/links?limit=20 to list the authenticated owner's links, and GET /r/{code} to redirect. A link record contains a code, owner_id, destination and creation time. The server derives owner_id from authentication. It must still check authorization when a caller reads, changes or deletes a particular link: being logged in does not grant access to every record.
Read messages as contracts
A creation request carries Content-Type: application/json and a body such as {"url":"https://example.com/guide"}. A successful response can be 201 Created with a Location header naming the new resource. GET is safe: its requested semantics do not change server state. PUT and DELETE are idempotent in their intended effects, though repeated responses may differ. POST is not inherently idempotent. GET can still produce logs or metrics; ‘safe’ describes the requested action, not the absence of every incidental write.
Use status codes to make failure understandable
Use 400 for a malformed request, 401 when authentication is required or invalid, 403 for insufficient permission, and 404 for a missing resource or a deliberate policy that hides existence. A conflicting idempotency payload can use 409. Rate limits use 429; overloaded or unavailable services can use 503. A client should respect retry guidance and use bounded exponential backoff with jitter. Blindly retrying all 4xx errors wastes capacity and usually cannot repair the request.
Choose redirects and caching deliberately
A redirect returns a 3xx status plus Location. If destinations can change, a temporary redirect such as 302 with an explicit cache policy avoids promising permanent mapping. Permanent redirects can be cached aggressively. Validate allowed URL schemes and protect management endpoints. Do not build a server-side URL preview fetcher without additional controls: fetching arbitrary user destinations is a different security problem from returning a redirect.
Write an API table before implementation
For a notes service, POST /v1/notes creates a note, GET /v1/notes/{id} reads it and DELETE removes it. A list endpoint returns a bounded page. Derive owner identity from a verified session and apply authorization to every individual object request. An opaque-looking identifier is not a permission check.
Write the request schema and one success response, then enumerate failure categories. A malformed JSON body, expired session, forbidden edit, missing object and temporary outage require different handling. Stable machine-readable error codes let clients respond without parsing prose.
Choose semantics deliberately
Idempotent means repeating the same operation has the same intended effect; it does not require identical status codes. A first DELETE may return success and a second may report missing while the resource remains deleted in both cases. PUT replaces the state at a known resource; PATCH applies a change whose idempotency depends on the patch operation.
For mutable short links, a permanent redirect can cause clients to reuse an old destination. State the redirect and cache policy together. A temporary redirect alone does not mean no cache can ever store it; explicit freshness rules make the intended behavior clearer.
Version the contract, not only the URL
Adding an optional response field may be compatible with tolerant clients, while removing a field or changing its meaning can break them. Keep error shapes stable and define input limits. Bound URL length, page size and request-body size so one valid-looking request cannot consume unbounded work.
Document the measurement boundary too: an endpoint returning 202 may mean work was accepted, not completed. The caller then needs a way to inspect progress or receive a completion event. Promise what the response actually establishes.
Guided tutorial
1. Make an endpoint table
List method, path, permission, body, success and failure for create/read/delete.
2. Write three errors
Produce consistent JSON errors for invalid input, unauthenticated access and a conflict.
3. Inspect methods
Explain safe versus idempotent using GET and DELETE.
4. Review evolution
Add a new optional field and describe how an older client should behave.
POST /v1/notes → 201 Created + Location
GET /v1/notes/n7 → 200 or documented failure
DELETE /v1/notes/n7 → deletion outcome
error body: {"code":"permission_denied","message":"..."}
owner_id comes from authentication, not the request body04 Cheat sheet
Review the decisions
Use this table to explain the mechanism and its limit without memorizing a product name.
| Choice or concept | Remember | Check |
|---|---|---|
| GET | Read a resource | Safe and idempotent semantics |
| POST | Create or invoke | Needs an explicit retry contract |
| PUT | Replace at a known resource | Idempotent intended effect |
| DELETE | Remove a resource | Repeated status codes may differ |
05 Where & why
Check the primary references
Use official references for protocol and implementation details. The questions here are teaching prompts.
06 Interview questions
Explain the reasoning out loud
Answer before opening the explanation. The questions progress from vocabulary to decisions and failure behavior.
What does Resource mean?
What does Safe method mean?
What does Authorization mean?
Is DELETE idempotent if the second response differs?
Does an unpredictable id authorize access?
What does 202 establish?
Why use structured error codes?
Can PATCH always be retried?
07 Practice problems
Practice a new case
Solve the new cases before revealing hints. Complete worked solutions follow the set.
Create outcome
EasyShow the hint
Permission failure
EasyShow the hint
Retry semantics
MediumShow the hint
Async work
MediumShow the hint
Bound a request
MediumShow the hint
Review a mutable redirect
HardShow the hint
Worked solutions
1. Create outcome
201 Created with a body containing the task id and a Location header is a clear contract.
2. Permission failure
Return the documented permission failure, commonly 403, or a deliberate privacy-preserving 404 policy. Enforce it on the server.
3. Retry semantics
Setting the same value is idempotent under that contract; an increment repeats its effect unless deduplicated.
4. Async work
Return 202 with an operation id and status URL. Define pending, succeeded, failed and expiry behavior for the status resource.
5. Bound a request
Limit page size, query length/complexity and execution time. Also cap body size and enforce authorization filters on all pages.
6. Review a mutable redirect
Use a documented temporary redirect and explicit cache policy consistent with the update visibility requirement. Define the update response, authorization, invalidation path and behavior of already-in-flight reads.