REST API Design & Constraints

Understand the structural constraints of Representational State Transfer (REST), including statelessness, cacheability, and the uniform interface.

BeginnerAPI DesignChapter: API Design10 min read

What is REST?

Representational State Transfer (REST) is an architectural style designed by Roy Fielding in 2000 to govern the design of network-based software architectures, specifically the World Wide Web.

REST is not a protocol, a language, or a coding standard. Instead, it is a set of six architectural constraints that, when applied together, ensure applications scale efficiently, remain highly cacheable, and adapt to changing data representations over time.

Real-World Analogy

Imagine walking into a fast-food restaurant.

  • The Client-Server model is the clear separation between you (the customer ordering) and the kitchen (cooking the food). You don't care how the kitchen cleans pots, and the kitchen doesn't care how you eat your burger.
  • Statelessness means when you order, you must list your entire order from scratch. If you want a drink and a burger, you can't say "and a drink" in a second conversation; the cashier has no memory of you.
  • Cacheability is like the restaurant keeping prepared burgers under a heat lamp. If a client orders one, the cashier checks the freshness label (ETag) and hands it over immediately instead of asking the kitchen to cook a new one from scratch.
  • A Layered System means when you stand at the counter, you cannot tell if the server is cooking in the back, picking up food from a sister kitchen next door, or routing your payment to a separate bank proxy.

The Six REST Constraints

To qualify as a RESTful API, a system must adhere to the following architectural constraints:

1. Client-Server Separation

The client (user interface) and the server (data storage, state, and business logic) are completely decoupled. This separation allows developers to update mobile apps or web frontends without modifying backend services, and scale database systems independently of user interfaces.

2. Statelessness

The server must not store any session state about the client in its memory. Every HTTP request must be entirely self-contained, carrying all metadata, authentication tokens, and request parameters necessary for the server to process it. If a server instance crashes, any other server instance can process the next request immediately, enabling horizontal scalability.

3. Cacheability

All server responses must declare whether they are cacheable. Caching information prevents clients from repeatedly fetching unchanging resources, reducing network traffic and database queries. Cache behaviors are defined using HTTP headers:

  • Cache-Control: Configures parameters like public, private (client only), no-cache (must validate before serving), no-store (never cache), and max-age (validity duration in seconds).
  • ETag (Entity Tag): A unique fingerprint (like a SHA-256 hash) of a resource representation. When a client requests the resource again, it sends the ETag in the If-None-Match header. If the resource hasn't changed, the server returns a bodyless 304 Not Modified status code, saving bandwidth.
  • Last-Modified: A timestamp indicating when the resource last changed.

4. Layered System

A client cannot assume it is communicating directly with the application server. The architecture may route requests through load balancers, security firewalls, caching proxies (CDNs), and gateways. These intermediaries must not alter request semantics.

5. Uniform Interface

This constraint is the defining characteristic of REST, establishing a standard contract between client and server. It consists of four sub-constraints:

  • Resource Identification in URIs: Resources are nouns (e.g. /items/42), not verbs or actions.
  • Resource Manipulation through Representations: The client interacts with resources via abstract representations (like JSON or XML payloads). The client can modify or delete resources if they have appropriate permissions and representation details.
  • Self-Descriptive Messages: Every message contains enough metadata to describe how to process it. For example, the Content-Type header (like application/json) explicitly tells the parser how to decode bytes.
  • HATEOAS (Hypermedia As the Engine of Application State): Server responses return not just raw data, but hypermedia links navigating users to related actions (e.g. including a "rel": "update" link in a response so the client knows how to modify the resource dynamically).

6. Code-on-Demand (Optional)

Servers can temporarily extend client capabilities by transmitting executable scripts (like JavaScript or WebAssembly) directly to the browser or runtime.


REST Interaction and the Layered System

By combining statelessness, layered architectures, and cache validation, REST systems handle massive transaction volumes:

Fielding REST Constraints & ETag Validation Client UI / Browser Client-Server Decoupled Concerns Layered System Ingress Load Balancer Traffic Gateway Reverse Proxy Cache validation Web Server REST Application Stateless Server No Session State 1. GET /items/1 (Stateless Request) 2. 200 OK + ETag: "a1b2c3" (Cache-Control: public) 3. GET /items/1 [If-None-Match: "a1b2c3"] 4. 304 Not Modified (Server not queried!)

Safe vs. Idempotent HTTP Methods

In REST, HTTP methods are classified based on their side-effects on the server's resource state:

  • Safe Methods: Do not modify server resource state. Safe methods are read-only and include GET, HEAD, and OPTIONS. Because they are safe, browsers and proxies can cache their responses freely.
  • Idempotent Methods: Calling the method multiple times results in the same server state as calling it once. For example, PUT /items/1 sets the resource's state; running it 5 times leaves the item with the exact same values. DELETE /items/1 is also idempotent: once deleted, the item remains gone. Safe methods are also idempotent by default.
  • Non-Idempotent Methods: Every execution can change server state. POST /items creates a new resource on every invocation. Calling it 5 times creates 5 separate resources. PATCH is generally non-idempotent because a partial update can perform relative operations (like appending string data or incrementing values).

Semantic HTTP Status Codes

RESTful APIs use HTTP response status codes to communicate execution outcomes, mapping errors to their appropriate semantic class:

  • 201 Created: The server successfully created a resource. The response must include a Location header containing the URL to the new resource.
  • 304 Not Modified: Sent during cache validation (when If-None-Match or If-Modified-Since checks succeed), instructing the client to display their cached copy. The body is omitted to save network bandwidth.
  • 412 Precondition Failed: The server rejected the request because one or more conditional headers (e.g. If-Match matching a resource ETag) failed validation, preventing concurrent write collisions.
  • 415 Unsupported Media Type: The server rejected the request because the request payload format (specified in Content-Type) is not supported.

A common anti-pattern is returning 200 OK with an error message (like {"error": "Unauthorized"}) inside the body. RESTful APIs must return the correct status code (e.g. 401 Unauthorized) to allow intermediaries and standard clients to handle errors natively.


Further Reading

Prerequisites

Code Examples

Core Literature References

Architectural Styles and the Design of Network-based Software Architectures

by Roy Thomas Fielding — Chapter 5: Representational State Transfer (REST), pp. 76-107

View source