Background
Sections
IntroductionRequirements & Problem AnalysisConstraints & AssumptionsEstimation TechniquesFunctional vs Non-Functional RequirementsMoSCoW PrioritizationSystem Design FundamentalsArchitecture DiagramClass DiagramComponent DiagramData Flow Diagram (DFD)ER Diagram (Entity-Relationship Diagram)High Level Design (HLD)Low Level Design (LLD)Sequence DiagramState DiagramUse Case DiagramData StorageDocument StoresFile StorageGraph DatabasesIn-Memory DatabasesKey-Value StoresNewSQLNoSQL DatabasesObject StorageSQL Databases (RDBMS)Time-Series DatabasesWide-Column StoresDatabase ConceptsACID PropertiesCAP TheoremConsistency ModelsIndexingNormalization & DenormalizationReplicationSharding & PartitioningTransactions & Isolation LevelsScalabilityAuto-Scaling & ElasticityConsensus & Leader ElectionLoad BalancingReplication & Read ReplicasSharding & PartitioningVertical vs Horizontal ScalingAvailability & ReliabilityBackup & Data DurabilityCircuit BreakerData ConsistencyDisaster RecoveryFault Tolerance & FailoverGraceful DegradationHigh AvailabilityNetworkingCDNDNSFirewalls & VPNHTTP & HTTPSLoad Balancer & Reverse ProxyTCP/IP & UDPWebSocketsCachingCache InvalidationCache Read/Write PatternsCaching LayersEviction PoliciesRedis vs MemcachedMessaging & CommunicationDead-Letter QueueIdempotencyKafka vs RabbitMQ vs SQSMessage QueuesPub/SubCompute & ServicesAPI GatewayContainers & OrchestrationMonolith vs MicroservicesServerlessService DiscoveryService MeshWeb Server & App ServerAPI DesignAPI Versioning & IdempotencyAuthentication & AuthorizationGraphQLgRPCPaginationRate Limiting & ThrottlingRESTSecurityAuthentication & AuthorizationData PrivacyEncryptionInput Validation & InjectionOAuth2 & JWTSecrets ManagementXSS & CSRFStorage & File SystemsBackup & RetentionBlock vs File vs Object StorageData Lakes & WarehousesDistributed File SystemsEphemeral StorageObservability & MonitoringDistributed TracingHealth ChecksLoggingMetricsSLI, SLO, SLADesign PatternsBulkhead PatternCircuit Breaker PatternCreational PatternsRate Limiter PatternRetry PatternStructural & Behavioral Patterns

REST

7 min read

In a Nutshell

REST (Representational State Transfer) is an architectural style for designing networked APIs around resources — nouns like users, orders, products — that you manipulate using standard HTTP methods. Instead of calling arbitrary functions, you GET a resource to read it, POST to create one, PUT/PATCH to update, and DELETE to remove it. REST's genius is that it leans entirely on the existing machinery of HTTP: URLs identify resources, methods define actions, status codes report outcomes, and caching/proxies work for free. This simplicity and universality made REST the default style for web APIs — it's the baseline every backend engineer must know cold.

2D minimalistic diagram showing a client interacting with a resource "/orders/42" via four labeled HTTP methods: GET (read, arrow returning data), POST (create, arrow adding a new item), PUT/PATCH (update, arrow modifying), and DELETE (remove, arrow deleting), each mapped to a CRUD operation on the resource

How It Actually Works

Resources and Methods: The Core Mapping

REST models everything as resources addressed by URLs, acted on by HTTP methods (see HTTP & HTTPS):

Method Action Example Idempotent?
GET Read GET /orders/42 Yes
POST Create POST /orders No
PUT Replace (full) PUT /orders/42 Yes
PATCH Update (partial) PATCH /orders/42 No (usually)
DELETE Remove DELETE /orders/42 Yes
Resource-oriented URLs (nouns, not verbs):
  ✅ GET  /users/42/orders        (get user 42's orders)
  ✅ POST /users/42/orders        (create an order for user 42)
  ❌ GET  /getUserOrders?id=42    (verb in URL — not RESTful)
  ❌ POST /createOrder            (RPC-style, not resource-oriented)

The REST Design Principles

Principle Meaning
Client-server Separation of concerns; they evolve independently
Stateless Each request contains everything needed; server keeps no session
Cacheable Responses declare cacheability (leverage HTTP caching)
Uniform interface Consistent resource URLs + standard methods + status codes
Layered system Proxies, gateways, LBs can sit between transparently

Statelessness is the most consequential: because the server holds no per-client state, any server can handle any request — the foundation of horizontal scaling (see Vertical vs Horizontal Scaling).

Status Codes: Communicating Outcomes

Using the right status code is part of good REST design — clients and proxies rely on them:

2xx success:   200 OK, 201 Created, 204 No Content
3xx redirect:  301 Moved, 304 Not Modified (caching)
4xx client:    400 Bad Request, 401 Unauthorized, 403 Forbidden,
               404 Not Found, 409 Conflict, 422 Unprocessable, 429 Too Many
5xx server:    500 Internal Error, 502 Bad Gateway, 503 Unavailable

Richardson Maturity Model

A useful ladder for "how RESTful" an API is:

Level Description
0 HTTP as a tunnel for RPC (one endpoint, POST everything)
1 Resources (distinct URLs per resource)
2 HTTP verbs + status codes used properly (most "REST" APIs live here)
3 HATEOAS — responses include links to related actions (rarely fully adopted)

Most production "REST" APIs are Level 2 — resource URLs plus correct methods and status codes — and that's usually the pragmatic target.

Good REST Design Conventions

  • Nouns, plural, hierarchical: /users/42/orders/7, not verbs.
  • Filtering/sorting/pagination via query params: /orders?status=shipped&sort=-created&page=2.
  • Versioning: /v1/orders or a header (see API Versioning & Idempotency).
  • Consistent errors: a structured error body ({ "error": { "code": "...", "message": "..." } }).
  • Use standard status codes rather than always returning 200 with an error flag.

REST's Limitations

REST isn't perfect, which is why GraphQL and gRPC exist:

  • Over-fetching — an endpoint returns more data than the client needs.
  • Under-fetching / N+1 — the client must make many calls to assemble a view (fetch user, then each of their orders, then each order's items).
  • Rigid responses — the server decides the shape; clients can't ask for exactly what they need. (This is precisely what GraphQL addresses.)

2D minimalistic diagram illustrating REST's over-fetching and under-fetching problems: on one side a mobile client receiving a large response with many unused fields greyed out (over-fetching), on the other side the same client making three sequential requests to assemble one screen (under-fetching / N+1), with a note that the server dictates the response shape

Seeing It in Action

Scenario: A well-designed REST API for an orders resource.

# List with filtering, sorting, pagination
GET /v1/users/42/orders?status=shipped&sort=-created_at&page=2&limit=20
→ 200 OK
  { "data": [ {...}, {...} ],
    "pagination": { "page": 2, "limit": 20, "total": 137,
                    "next": "/v1/users/42/orders?...&page=3" } }

# Create — returns 201 with a Location header
POST /v1/users/42/orders
  { "items": [ { "product_id": 9, "qty": 2 } ] }
→ 201 Created
  Location: /v1/users/42/orders/7
  { "id": 7, "status": "pending", ... }

# Partial update
PATCH /v1/orders/7
  { "status": "cancelled" }
→ 200 OK   { "id": 7, "status": "cancelled", ... }

# Conditional GET leverages HTTP caching
GET /v1/orders/7
  If-None-Match: "v3-abc"
→ 304 Not Modified          (nothing re-sent; cache still valid)

# Errors use proper status codes + structured bodies
POST /v1/users/42/orders   (empty items)
→ 422 Unprocessable Entity
  { "error": { "code": "empty_order", "message": "Order must have items" } }

Why these conventions matter: the API is predictable — anyone who knows REST can guess that GET /v1/orders/7 reads order 7 and DELETE removes it, without reading docs. It's cache-friendly (ETags + 304 avoid redundant transfers), scalable (stateless — any server serves any request), and tooling-friendly (proxies, gateways, and HTTP clients all understand the methods and status codes for free). This is REST's enduring value: by conforming to HTTP's existing semantics, you inherit an entire ecosystem of caching, routing, and tooling with zero extra work.

Interview Questions

  1. Q: What makes an API RESTful, and what does statelessness buy you? Hint: Resources addressed by URLs (nouns), manipulated via standard HTTP methods (GET/POST/PUT/PATCH/DELETE) with proper status codes, plus a uniform interface, cacheability, and a layered system. Statelessness — each request is self-contained and the server keeps no session — means any server can handle any request, enabling horizontal scaling, easy load balancing, and resilience (no lost sessions on server failure).

  2. Q: How do you design resource-oriented URLs? Give good and bad examples. Hint: Use plural nouns and hierarchy, not verbs: GET /users/42/orders (good) vs GET /getUserOrders?id=42 or POST /createOrder (bad — verbs/RPC style). Actions come from HTTP methods, not the URL. Filtering/sorting/pagination go in query params (/orders?status=shipped&page=2). The URL identifies the resource; the method defines the action.

  3. Q: What are over-fetching and under-fetching, and how do they motivate alternatives to REST? Hint: Over-fetching: an endpoint returns more fields than the client needs (wasted bandwidth). Under-fetching / N+1: the client must make many calls to assemble one view (fetch user → each order → each item). Both stem from the server dictating fixed response shapes. GraphQL addresses them by letting clients request exactly the fields and nesting they need in one query.

  4. Q: What is the Richardson Maturity Model, and where do most REST APIs sit? Hint: A ladder: Level 0 (HTTP as an RPC tunnel), Level 1 (distinct resource URLs), Level 2 (proper HTTP verbs + status codes), Level 3 (HATEOAS — hypermedia links to related actions). Most production "REST" APIs are Level 2 — resource URLs with correct methods and status codes — which is the pragmatic sweet spot; full HATEOAS is rarely adopted.

  5. Q: Why does using correct HTTP status codes and methods matter beyond aesthetics? Hint: Clients, proxies, caches, and gateways rely on them. Idempotent methods (GET/PUT/DELETE) can be safely retried; cacheable GETs with 304 avoid redundant transfers; 4xx vs 5xx tells clients whether to fix the request or retry; 429 + Retry-After enables intelligent backoff. Conforming to HTTP semantics means you inherit the entire ecosystem of caching, retry, and routing behavior for free.

References

Dive Deeper