
REST Constraints: Statelessness, Cacheability, and the Uniform Interface
Understand the six architectural constraints that define REST. Learn why statelessness enables scale, how caching improves performance, and what makes an interface uniform.
REST Constraints: Statelessness, Cacheability, and the Uniform Interface
REST is not a protocol or a standard — it is a set of architectural constraints. Roy Fielding defined six constraints in his 2000 doctoral dissertation that a system must follow to be considered RESTful. Understanding these constraints helps you evaluate APIs, debug issues, and appreciate why REST scales so well.
1. The Six REST Constraints
graph TD
A[REST Architecture] --> B["1. Client-Server"]
A --> C["2. Stateless"]
A --> D["3. Cacheable"]
A --> E["4. Uniform Interface"]
A --> F["5. Layered System"]
A --> G["6. Code on Demand (Optional)"]
style A fill:#4f46e5,color:#fff
style B fill:#0891b2,color:#fff
style C fill:#059669,color:#fff
style D fill:#059669,color:#fff
style E fill:#059669,color:#fff
style F fill:#0891b2,color:#fff
style G fill:#6b7280,color:#fff
2. Constraint 1: Client-Server Separation
The client and server are independent. They can be developed, deployed, and scaled separately.
| Client Responsibilities | Server Responsibilities |
|---|---|
| User interface | Data storage |
| User input handling | Business logic |
| Display formatting | Data validation |
| Platform-specific code | Authentication |
Why it matters: A mobile app, web app, and CLI tool can all use the same REST API. The server does not need to know anything about the client's interface.
3. Constraint 2: Statelessness
Every request must contain all information needed to process it. The server does not store any client state between requests.
What Stateless Means
sequenceDiagram
participant Client
participant Server
Client->>Server: GET /profile<br/>Auth: Bearer token123
Server-->>Client: Profile data
Note right of Server: Server forgets<br/>everything about<br/>the previous request
Client->>Server: GET /orders<br/>Auth: Bearer token123
Note right of Server: Server does NOT<br/>know who this is.<br/>Must validate token again.
Server-->>Client: Orders data
Stateless vs Stateful
Stateful (NOT REST):
Client: Login as Jane → Server: OK, I remember you
Client: Get my profile → Server: Sure, you are Jane (from memory)
Client: Get my orders → Server: Here are Jane's orders (from memory)
Stateless (REST):
Client: Get profile + token → Server: Token says Jane, here is her profile
Client: Get orders + token → Server: Token says Jane, here are her orders
Each request is self-contained. The server validates the token fresh every time.
Why Statelessness Enables Scale
With stateful servers, if Server A handles your login and remembers you, all your future requests must go to Server A. This creates a bottleneck.
With stateless servers, any server can handle any request:
graph TD
C[Client] --> LB[Load Balancer]
LB --> S1[Server 1]
LB --> S2[Server 2]
LB --> S3[Server 3]
S1 -.->|"Any server can<br/>handle any request"| S2
S2 -.-> S3
style LB fill:#4f46e5,color:#fff
style S1 fill:#059669,color:#fff
style S2 fill:#059669,color:#fff
style S3 fill:#059669,color:#fff
You can add or remove servers freely. If Server 2 crashes, the next request just goes to Server 1 or 3 with no data loss.
4. Constraint 3: Cacheability
Responses should indicate whether they can be cached and for how long. This dramatically reduces server load and improves performance.
How Caching Works
graph LR
A[Client Request] --> B{In Cache?}
B -->|Yes, fresh| C[Return Cached<br/>No server call]
B -->|No or stale| D[Request from Server]
D --> E[Response with<br/>Cache-Control header]
E --> F[Store in Cache]
F --> C
style C fill:#059669,color:#fff
style D fill:#d97706,color:#fff
Cache-Control Examples
Cache-Control: max-age=3600 # Cache for 1 hour
Cache-Control: no-cache # Always revalidate
Cache-Control: no-store # Never cache (sensitive data)
Cache-Control: public, max-age=86400 # Cache publicly for 24 hours
What Should Be Cached?
| Resource | Cacheable? | Why |
|---|---|---|
| Product listing | Yes | Changes infrequently |
| User profile | Short cache | May change |
| Authentication tokens | No | Sensitive |
| Search results | Short cache | Dynamic |
| Static assets (images) | Yes, long | Rarely changes |
| Real-time data (stock prices) | No | Constantly changes |
Caching is one of the biggest performance wins in API architecture. A well-cached API can reduce server load by 90% or more.
5. Constraint 4: Uniform Interface
The uniform interface is REST's most important constraint. It means every resource is accessed in the same, consistent way.
Four Sub-Constraints
1. Resource Identification through URIs
Every resource has a unique URL:
/users/42 ← User 42
/posts/7 ← Post 7
/orders/300 ← Order 300
2. Resource Manipulation through Representations
Clients receive a representation of the resource (JSON), not the resource itself. They modify the resource by sending back a modified representation.
3. Self-Descriptive Messages
Each request/response contains all the information needed to understand it:
- Method tells the action
- Headers tell the format
- Status code tells the result
4. HATEOAS (Hypermedia as the Engine of Application State)
Responses include links to related actions:
{
"id": 42,
"name": "Jane Doe",
"links": {
"self": "/users/42",
"posts": "/users/42/posts",
"orders": "/users/42/orders",
"delete": "/users/42"
}
}
The client discovers available actions from the response itself. In practice, most APIs do not fully implement HATEOAS, but it is the ideal.
6. Constraint 5: Layered System
The client does not need to know if it is talking directly to the server or through intermediaries (proxies, load balancers, CDNs, API gateways).
graph LR
A[Client] --> B[CDN]
B --> C[API Gateway]
C --> D[Load Balancer]
D --> E[API Server]
A -.->|Client thinks it is<br/>talking to one server| E
style A fill:#0891b2,color:#fff
style B fill:#6b7280,color:#fff
style C fill:#6b7280,color:#fff
style D fill:#6b7280,color:#fff
style E fill:#4f46e5,color:#fff
This enables:
- CDNs for caching close to users
- API Gateways for rate limiting and authentication
- Load Balancers for distributing traffic
- Proxies for security and monitoring
7. Constraint 6: Code on Demand (Optional)
The server can optionally send executable code (like JavaScript) to the client. This is the only optional constraint and is rarely used in REST APIs. Web browsers downloading JavaScript from a server is an example.
8. How to Evaluate If an API Is RESTful
| Constraint | Question to Ask | Example |
|---|---|---|
| Client-Server | Are frontend and backend separate? | Yes if API serves multiple clients |
| Stateless | Does each request include all needed info? | Yes if auth token in every request |
| Cacheable | Are cache headers present? | Check Cache-Control in responses |
| Uniform Interface | Are URLs consistent and resource-based? | /users/42 not /getUserById |
| Layered | Can you add a load balancer transparently? | Yes if stateless |
Most real-world APIs are partially RESTful. Very few implement every constraint perfectly, and that is fine. The constraints are ideals to aspire to.
Summary and Key Takeaways
- REST has six constraints: client-server, stateless, cacheable, uniform interface, layered, code-on-demand.
- Statelessness is the key to scalability — any server can handle any request.
- Caching can reduce server load by 90%+ when implemented correctly.
- The uniform interface ensures consistency: same URL patterns, same methods, same response formats.
- Layered systems enable CDNs, load balancers, and API gateways transparently.
- Most APIs are partially RESTful — following most but not all constraints.
Lesson Review Quiz
?Knowledge Check
Why is statelessness critical for API scalability?
?Knowledge Check
What does the Cache-Control: no-store header mean?
?Knowledge Check
What is HATEOAS?