REST APIs
REST APIs
REST (Representational State Transfer) is the dominant architectural style for building HTTP-based APIs. First described by Roy Fielding in his 2000 doctoral dissertation, it is not a protocol or a standard — it is a set of constraints that, when followed, produce systems that are scalable, simple to understand, and easy to evolve. Almost every public API you have consumed — Twitter, Stripe, GitHub, Twilio — is a REST API.
The Six REST Constraints
Fielding defined six constraints. Two are the most important for everyday design:
- Uniform Interface — Resources are identified by stable URIs; interactions happen through representations (JSON, XML) and standard HTTP methods.
- Statelessness — Every request contains all the information the server needs. The server holds no session state between requests.
- Client–Server separation — UI concerns are decoupled from data-storage concerns.
- Cacheability — Responses declare whether they may be cached, enabling proxies and clients to cache aggressively.
- Layered System — A client cannot tell if it is talking directly to the server or through an intermediary (load balancer, CDN, gateway).
- Code on Demand (optional) — Servers may send executable code (JavaScript) to clients.
Resources and URIs
In REST, a resource is any concept that can be named and addressed. A user is a resource. An order is a resource. A photo album is a resource. Resources are nouns, not verbs.
A URI (Uniform Resource Identifier) names the resource. URI design is the first place REST APIs go wrong. Follow these rules:
- Use nouns, not verbs.
/usersnot/getUsers./orders/42/cancelis borderline — better asPOST /orders/42/cancellations(treating the cancellation itself as a resource). - Use plural nouns for collections.
/articles,/products,/invoices. - Nest to express ownership, but limit to two levels.
/users/7/postsis fine./users/7/posts/3/comments/1/likesis a maintenance nightmare — flatten to/likes/99. - Use kebab-case for multi-word segments.
/order-itemsnot/orderItemsor/order_items. - Keep query parameters for filtering, sorting, and pagination.
GET /products?category=electronics&sort=price&page=2.
HTTP Verbs
HTTP methods (verbs) express the intent of the operation. Using them correctly enables caches, proxies, and clients to behave predictably without reading documentation.
Idempotency and Why It Matters
An operation is idempotent if performing it multiple times produces the same result as performing it once. This matters enormously in distributed systems because networks are unreliable — clients retry failed requests.
DELETE /orders/42called twice: first call deletes the order, second call gets a404. The order is still gone — idempotent. ✓POST /orderscalled twice due to a network timeout: you may create two orders — not idempotent. Use an idempotency key header (Idempotency-Key: <uuid>) to let the server deduplicate.PUT /orders/42with a full body: replaces the resource to the same state each time — idempotent. ✓
Idempotency-Key header on POST requests. The server stores the key and, if it sees a duplicate within 24 hours, returns the original response without charging the card again. Adopt this pattern for any non-idempotent endpoint that handles money, emails, or other irreversible side-effects.
HTTP Status Codes
Status codes communicate the outcome of a request. Use them precisely — do not return 200 OK with an error message in the body.
- 2xx Success —
200 OK,201 Created(include aLocationheader to the new resource),204 No Content(for DELETE with no body). - 3xx Redirection —
301 Moved Permanently,304 Not Modified(enables client-side caching viaETag/If-None-Match). - 4xx Client Error —
400 Bad Request,401 Unauthorized(not authenticated),403 Forbidden(authenticated but not allowed),404 Not Found,409 Conflict,422 Unprocessable Entity(validation errors),429 Too Many Requests(rate limiting). - 5xx Server Error —
500 Internal Server Error,503 Service Unavailable(use for circuit-breaker responses).
Statelessness at Scale — A Real-World Flow
The following diagram shows a stateless REST request flowing through a production-grade system. Notice that the load balancer can send any request to any application server — there is no sticky session because all state is in the database and token.
Designing Clean REST APIs — Practical Guidelines
Beyond the basics, several practices separate APIs that developers love from those they dread:
- Version from day one. Use a URL prefix (
/v1/,/v2/) or anAcceptheader. Changing a response shape without versioning breaks every client that exists. - Return consistent error shapes. Every error response should have the same JSON structure:
error.code,error.message, optionalerror.details. Stripe and GitHub do this perfectly. - Paginate collections. Never return unbounded lists. Use cursor-based pagination for high-volume feeds (
?after=cursor) and offset/limit for simple tables (?page=3&per_page=25). - Use HATEOAS selectively. Hypermedia As The Engine Of Application State means embedding links in responses (
"links": {"self": "/orders/42", "cancel": "/orders/42/cancellations"}). Useful for discoverability; skip it if clients are tightly coupled mobile apps you fully control. - Expose only what clients need. Avoid returning 40-field objects when the mobile app uses 5. Consider sparse fieldsets (
?fields=id,name,price) or GraphQL if over-fetching is a real cost.
POST /createOrder, GET /getUser?id=7, or POST /deleteProduct are not REST — they are RPC over HTTP. They forgo caching, break reverse proxies that optimise by verb, and confuse consumers. If you find yourself putting verbs in URLs, stop and model the resource instead.
REST vs. "REST-ish"
In practice, very few public APIs satisfy all six Fielding constraints — particularly HATEOAS. What most developers call a "REST API" is more accurately a resource-oriented HTTP API. That is fine. The constraints that matter most at scale are statelessness (for horizontal scaling), the uniform interface (for cacheability and predictability), and correct use of HTTP verbs and status codes. Start there.
The next two lessons will cover RPC/gRPC (where REST's verbosity becomes a bottleneck in internal microservice calls) and WebSockets (where REST's request-response model breaks down for real-time data). Knowing REST deeply makes those trade-offs legible.