Introduction to REST
REST (Representational State Transfer) is an architectural style for designing networked applications, particularly web services. Introduced by Roy Fielding in his 2000 doctoral dissertation, REST has become the dominant approach for building APIs that power modern web and mobile applications.
Think of REST as a set of architectural guidelines rather than a strict protocol. It's like the principles of good urban planning that shape how a city functions, rather than specific building codes.
At its core, REST is about creating standardized, stateless interactions between clients and servers that leverage the existing features of HTTP to create scalable, maintainable, and interoperable services.
The Evolution of Web APIs
To understand the significance of REST, it helps to consider the evolution of web service architectures:
Early Web (1990s)
The early web primarily consisted of static HTML pages served to browsers. Interactivity was limited, and most "dynamic" content required full page refreshes.
RPC and SOAP (Late 1990s - Early 2000s)
Remote Procedure Call (RPC) approaches and later Simple Object Access Protocol (SOAP) attempted to bring distributed computing concepts to the web. These technologies allowed for remote function calls but were often complex, verbose, and didn't align well with the web's architecture.
REST Emergence (2000s)
REST emerged as a simpler alternative that embraced HTTP's design rather than trying to layer complex protocols on top of it. By treating resources as the central concept and using standard HTTP methods, REST created a more web-friendly approach to APIs.
Modern API Landscape (2010s-Present)
Today, RESTful APIs dominate the web landscape, powering everything from social media platforms to banking systems. Extensions and variations like GraphQL have emerged for specific use cases, but REST remains the foundation of most web APIs.
Key Principles of REST
REST is defined by six core architectural constraints. Together, these principles create the properties that make RESTful systems scalable, maintainable, and effective.
1. Client-Server Architecture
The client and server are separate entities that evolve independently. This separation of concerns allows both to change their implementations without affecting the other as long as the interface between them remains consistent.
Real-world example: A mobile app (client) can communicate with a backend API (server) without knowing how the server stores or processes data. The server doesn't need to know about the client's UI or user experience.
2. Statelessness
Each request from a client to a server must contain all the information needed to understand and process the request. The server doesn't store any client state between requests.
Real-world example: When you request your bank account balance, you need to include your authentication credentials with each request—the server doesn't "remember" that you were logged in from a previous request.
This principle is analogous to visiting a doctor's office where you need to bring your ID and insurance card each time, regardless of how many times you've visited before.
3. Cacheability
Responses must define themselves as cacheable or non-cacheable to prevent clients from reusing stale or inappropriate data.
Real-world example: When requesting product information that rarely changes, the response can be cached to avoid redundant requests. However, real-time stock prices would be marked as non-cacheable.
This is similar to how newspapers (cacheable for days) differ from live sports scores (not cacheable for more than a few seconds).
4. Uniform Interface
The interface between client and server is standardized, simplifying the overall system architecture. This constraint has four sub-principles:
- Resource identification in requests: Resources are uniquely identified (e.g., by URLs)
- Resource manipulation through representations: Clients manipulate resources by sending representations (e.g., JSON)
- Self-descriptive messages: Each message includes enough information to describe how to process it
- Hypermedia as the engine of application state (HATEOAS): Clients interact with the application through hypermedia provided dynamically by the server
Real-world example: A shopping API uses URLs to identify products, JSON to represent them, and HTTP methods (GET, POST, PUT, DELETE) to define actions. Responses include links to related resources (like "add to cart" operations).
5. Layered System
A client cannot tell whether it is connected directly to the end server or to an intermediary. This allows for load balancing, shared caches, and security policies.
Real-world example: When using a social media platform, your requests might pass through CDNs, API gateways, load balancers, and several application servers—but you interact with it as if it were a single system.
This is similar to how you mail a letter to an address without needing to know about the sorting facilities, trucks, and mail carriers that will handle it along the way.
6. Code on Demand (Optional)
Servers can temporarily extend client functionality by transferring executable code. This constraint is optional in REST and is less commonly implemented than the others.
Real-world example: A web application downloading JavaScript that enhances the user interface or provides client-side validation.
Resources and Representations
The concept of resources is central to REST. Unlike RPC-style systems that focus on actions or procedures, REST focuses on resources—the nouns of your system.
What is a Resource?
A resource is any information that can be named and addressed. This could be:
- An entity (e.g., a user, product, or article)
- A collection of entities (e.g., all users)
- A concept (e.g., the current weather)
- A calculation or transformation (e.g., currency conversion)
Each resource is identified by a unique identifier, typically a URL in web-based REST systems.
# Examples of resource identifiers (URLs)
https://api.example.com/users/1234
https://api.example.com/products
https://api.example.com/articles/recent
https://api.example.com/weather/current?city=newyork
Resource Representations
A representation is the state of a resource at a specific moment, encoded in a specific format. The same resource can have multiple representations, such as:
- JSON (JavaScript Object Notation)
- XML (eXtensible Markup Language)
- HTML (HyperText Markup Language)
- Plain text
- Binary formats (e.g., PDF, images)
Content negotiation allows clients to request specific representations based on their capabilities and needs.
# JSON representation of a user resource
{
"id": 1234,
"name": "Jane Smith",
"email": "jane@example.com",
"created_at": "2025-01-15T14:30:00Z"
}
# XML representation of the same resource
<user>
<id>1234</id>
<name>Jane Smith</name>
<email>jane@example.com</email>
<created_at>2025-01-15T14:30:00Z</created_at>
</user>
This separation of resources from their representations is similar to how a physical building (resource) can be represented by photographs, blueprints, or 3D models (representations), each useful in different contexts.
Resource Relations
Resources often relate to each other. In RESTful systems, these relationships are typically expressed through:
- Nested URLs (e.g.,
/users/1234/ordersfor all orders belonging to user 1234) - References in representations (e.g., including a
user_idin an order representation) - Hyperlinks to related resources (HATEOAS principle)
# Order representation with references and links
{
"id": 5678,
"user_id": 1234,
"total": 99.99,
"status": "shipped",
"links": {
"self": "/orders/5678",
"user": "/users/1234",
"items": "/orders/5678/items"
}
}
HTTP in RESTful Services
REST leverages the HTTP protocol's existing features rather than inventing new mechanisms. This alignment with HTTP is a key factor in REST's simplicity and widespread adoption.
HTTP Methods as Resource Operations
RESTful APIs use standard HTTP methods to perform operations on resources:
| HTTP Method | CRUD Operation | Description | Example |
|---|---|---|---|
| GET | Read | Retrieve a resource or collection | GET /users/1234 |
| POST | Create | Create a new resource | POST /users |
| PUT | Update | Update an existing resource (complete replacement) | PUT /users/1234 |
| PATCH | Update | Partially update a resource | PATCH /users/1234 |
| DELETE | Delete | Remove a resource | DELETE /users/1234 |
This mapping is often compared to CRUD operations in databases, but it's important to note that REST is about resources, not database tables.
HTTP Status Codes
RESTful services use standard HTTP status codes to communicate the outcome of requests:
- 2xx (Success): Request was successful
- 200 OK: Standard success response
- 201 Created: Resource created successfully
- 204 No Content: Success with no response body (e.g., after DELETE)
- 3xx (Redirection): Further action needed
- 301 Moved Permanently: Resource has a new permanent URI
- 304 Not Modified: Resource hasn't changed (for conditional requests)
- 4xx (Client Error): Client made an error
- 400 Bad Request: Malformed request syntax
- 401 Unauthorized: Authentication required
- 403 Forbidden: Client is authenticated but not authorized
- 404 Not Found: Resource doesn't exist
- 405 Method Not Allowed: HTTP method not supported for this resource
- 422 Unprocessable Entity: Semantic errors in request
- 5xx (Server Error): Server failed to fulfill a valid request
- 500 Internal Server Error: Generic server error
- 503 Service Unavailable: Server temporarily unable to handle request
Status codes act like the standardized signals that train conductors use—they convey important information in a concise, universally understood format.
HTTP Headers
RESTful services make extensive use of HTTP headers for metadata and control information:
- Content negotiation: Headers like
AcceptandContent-Typedetermine the format of representations - Authentication:
Authorizationheader for credentials - Caching:
Cache-Control,ETag, andLast-Modifiedheaders for cache control - Rate limiting: Custom headers like
X-Rate-Limit-Limit - Pagination: Custom headers or link headers for navigating large collections
# Example HTTP request with headers
GET /users/1234 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
# Example HTTP response with headers
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: max-age=3600
ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"
X-Rate-Limit-Remaining: 59
{
"id": 1234,
"name": "Jane Smith",
"email": "jane@example.com"
}
Statelessness in Detail
Of all REST principles, statelessness has particularly far-reaching implications for API design and system architecture.
What Statelessness Means
In a stateless system, each request from a client to a server must contain all the information needed to understand and process that request. The server cannot rely on any stored context from previous requests.
This means:
- No session state is stored on the server
- Each request is processed in isolation
- The same request should produce the same response (with identical resources)
- Authentication/authorization information must be included with every request
Benefits of Statelessness
Statelessness provides several important advantages:
- Scalability: Servers can be easily added or removed as they don't need to share session state
- Reliability: Failures are easier to recover from as there's no state to reconstruct
- Visibility: Requests are self-contained, making monitoring and debugging easier
- Load balancing: Any server can handle any request, allowing for efficient distribution
Challenges and Solutions
Statelessness does introduce some challenges:
| Challenge | Solution |
|---|---|
| Authentication for multiple requests | Token-based auth (JWT, OAuth) instead of server sessions |
| Increased bandwidth for repetitive data | Efficient representations, HTTP compression |
| Multi-step operations | Resource-based design patterns (e.g., job resources) |
| Client needs to track application state | HATEOAS (hyperlinks in responses guide state transitions) |
Stateless Authentication Example
# Login request
POST /auth/login HTTP/1.1
Content-Type: application/json
{
"username": "janedoe",
"password": "securepassword"
}
# Login response with token
HTTP/1.1 200 OK
Content-Type: application/json
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"expires_in": 3600
}
# Subsequent authenticated request
GET /users/me HTTP/1.1
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Accept: application/json
In this pattern, the server doesn't store session information. Instead, the client receives a signed token containing the necessary authentication details and includes it with each subsequent request.
HATEOAS: Hypermedia as the Engine of Application State
HATEOAS is one of the more advanced principles of REST and is often overlooked in API implementations, yet it's critical for creating truly RESTful services.
The Concept of HATEOAS
HATEOAS means that clients interact with the application entirely through hypermedia provided dynamically by the server. Instead of having hardcoded knowledge of the API's structure, clients discover possible actions through links embedded in the responses.
Think of HATEOAS like exploring a website by clicking links, rather than memorizing specific URLs. The server tells the client what it can do next, rather than the client needing to know in advance.
Benefits of HATEOAS
- Evolvability: API endpoints can change without breaking clients
- Discoverability: Clients can learn about available operations
- Decoupling: Reduces client dependencies on specific URI structures
- State management: Guides clients through complex processes
HATEOAS Implementation Examples
There are several ways to implement HATEOAS in API responses:
# Example 1: Links section in JSON
{
"id": 1234,
"name": "Jane Smith",
"email": "jane@example.com",
"_links": {
"self": { "href": "/users/1234" },
"orders": { "href": "/users/1234/orders" },
"update": { "href": "/users/1234", "method": "PUT" },
"delete": { "href": "/users/1234", "method": "DELETE" }
}
}
# Example 2: HAL (Hypertext Application Language) format
{
"id": 1234,
"name": "Jane Smith",
"email": "jane@example.com",
"_links": {
"self": { "href": "/users/1234" },
"orders": { "href": "/users/1234/orders" }
}
}
# Example 3: JSON:API format
{
"data": {
"type": "users",
"id": "1234",
"attributes": {
"name": "Jane Smith",
"email": "jane@example.com"
},
"links": {
"self": "/users/1234"
},
"relationships": {
"orders": {
"links": {
"related": "/users/1234/orders"
}
}
}
}
}
HATEOAS for API Navigation
A HATEOAS-driven API allows a client to navigate the entire API starting from a single entry point:
# 1. Start at API root
GET /api HTTP/1.1
# Response:
{
"_links": {
"users": { "href": "/api/users" },
"products": { "href": "/api/products" },
"orders": { "href": "/api/orders" }
}
}
# 2. Navigate to users
GET /api/users HTTP/1.1
# Response:
{
"users": [
{
"id": 1234,
"name": "Jane Smith",
"_links": {
"self": { "href": "/api/users/1234" }
}
},
{
"id": 5678,
"name": "John Doe",
"_links": {
"self": { "href": "/api/users/5678" }
}
}
],
"_links": {
"self": { "href": "/api/users" },
"next": { "href": "/api/users?page=2" }
}
}
# 3. Navigate to specific user
GET /api/users/1234 HTTP/1.1
# Response has links to possible actions and related resources
This approach creates a self-documenting API where clients dynamically discover capabilities rather than relying on fixed endpoint knowledge.
REST vs. Other Architectural Styles
To better understand REST, it's helpful to compare it with other approaches to building web services.
REST vs. SOAP
| Aspect | REST | SOAP |
|---|---|---|
| Philosophy | Architectural style | Protocol specification |
| Message Format | Any format (typically JSON) | XML only |
| Transport | Typically HTTP, but flexible | Multiple (HTTP, SMTP, etc.) |
| Service Definition | No formal requirement (often OpenAPI) | WSDL (formal contract) |
| Statelessness | Required | Not required |
| Security | Uses HTTP mechanisms | WS-Security extensions |
| Bandwidth | Lightweight | More verbose |
| Learning Curve | Lower | Higher |
REST vs. GraphQL
| Aspect | REST | GraphQL |
|---|---|---|
| Request Model | Multiple endpoints | Single endpoint |
| Data Fetching | Fixed response structure | Client specifies needed fields |
| Under-fetching | Common (may need multiple requests) | Solved (client requests exactly what it needs) |
| Over-fetching | Common (get all resource fields) | Solved (client specifies fields) |
| Caching | Built into HTTP | Requires additional setup |
| Versioning | Typically in URL or header | Schema evolution without versions |
| Learning Curve | Medium | Steeper |
REST vs. gRPC
| Aspect | REST | gRPC |
|---|---|---|
| Communication | Request/response | Supports streaming (bidirectional) |
| Protocol | HTTP 1.1/2 | HTTP/2 |
| Format | Text-based (typically JSON) | Binary (Protocol Buffers) |
| Contract | Optional (OpenAPI) | Required (.proto files) |
| Code Generation | Optional | Core feature |
| Performance | Good | Excellent (smaller payloads) |
| Browser Support | Native | Limited (requires proxy) |
Each of these approaches has strengths and weaknesses. The right choice depends on the specific requirements of your project:
- REST: Excellent for public APIs, broad compatibility, and when HTTP features are valuable
- SOAP: Better for enterprise environments with formal contracts and complex transactions
- GraphQL: Ideal for complex frontends that need flexible data fetching and reducing network requests
- gRPC: Best for microservices, high-performance internal APIs, and polyglot environments
RESTful Anti-Patterns
Understanding what makes an API "not RESTful" can help clarify the principles. Here are common anti-patterns to avoid:
1. Verb-Based URLs
REST uses resources (nouns) as the focus, not actions (verbs).
| Non-RESTful (Avoid) | RESTful (Preferred) |
|---|---|
| GET /getUser/1234 | GET /users/1234 |
| POST /createUser | POST /users |
| POST /deleteUser/1234 | DELETE /users/1234 |
2. Ignoring HTTP Methods
Using only GET and POST for all operations undermines the RESTful model.
| Non-RESTful (Avoid) | RESTful (Preferred) |
|---|---|
| POST /users/1234/delete | DELETE /users/1234 |
| GET /users/1234/edit?name=NewName | PATCH /users/1234 with request body |
3. Session-Based Authentication
Using server-side sessions violates the statelessness principle.
| Non-RESTful (Avoid) | RESTful (Preferred) |
|---|---|
| POST /login to create server session | POST /auth to receive token |
| Requests include session cookie | Requests include Authorization header |
4. Multiple Endpoints for Different Format Representations
Content negotiation should be used instead of different endpoints for different formats.
| Non-RESTful (Avoid) | RESTful (Preferred) |
|---|---|
| GET /users/1234.json | GET /users/1234 with Accept header |
| GET /users/1234.xml |
5. Not Using Status Codes Properly
Always returning 200 OK with error information in the body.
| Non-RESTful (Avoid) | RESTful (Preferred) |
|---|---|
|
200 OK { "success": false, "error": "Resource not found" } |
404 Not Found { "message": "User with id 1234 not found" } |
6. Tight Coupling to URI Structure
Clients should not hard-code URI patterns (this breaks HATEOAS).
| Non-RESTful (Avoid) | RESTful (Preferred) |
|---|---|
| Client constructs URLs based on knowledge of pattern | Client follows links provided in responses |
7. Not Using Hypermedia Controls
Responses should include links to related resources and actions.
# Non-RESTful (Avoid)
{
"id": 1234,
"name": "Jane Smith",
"email": "jane@example.com"
}
# RESTful (Preferred)
{
"id": 1234,
"name": "Jane Smith",
"email": "jane@example.com",
"_links": {
"self": { "href": "/users/1234" },
"orders": { "href": "/users/1234/orders" }
}
}
Measuring RESTful Maturity: The Richardson Maturity Model
Leonard Richardson proposed a model for evaluating the "RESTfulness" of a web service, breaking it down into levels of maturity:
Level 0: The Swamp of POX (Plain Old XML)
At this level, services use HTTP as a simple transport protocol, often with a single endpoint and XML messages describing both the resource and the action.
# Level 0 example
POST /api HTTP/1.1
Content-Type: application/xml
<getUser>
<id>1234</id>
</getUser>
This approach essentially tunnels RPC calls through HTTP.
Level 1: Resources
At this level, services expose individual resources with unique URIs but might still use a single HTTP method (typically POST) for all operations.
# Level 1 example
POST /users/1234 HTTP/1.1
Content-Type: application/json
{
"action": "update",
"name": "Jane Smith"
}
This improves on Level 0 by introducing the concept of resources but doesn't leverage HTTP methods.
Level 2: HTTP Verbs
At this level, services use appropriate HTTP methods and status codes to perform operations on resources.
# Level 2 example
PUT /users/1234 HTTP/1.1
Content-Type: application/json
{
"name": "Jane Smith",
"email": "jane@example.com"
}
# Response
HTTP/1.1 200 OK
Content-Type: application/json
{
"id": 1234,
"name": "Jane Smith",
"email": "jane@example.com"
}
This level properly uses the HTTP protocol's capabilities, making the API more intuitive and allowing it to leverage HTTP infrastructure (caching, etc.).
Level 3: Hypermedia Controls (HATEOAS)
At this highest level, services include hyperlinks in their responses, enabling clients to discover available actions and navigate the API dynamically.
# Level 3 example
GET /users/1234 HTTP/1.1
Accept: application/json
# Response
HTTP/1.1 200 OK
Content-Type: application/json
{
"id": 1234,
"name": "Jane Smith",
"email": "jane@example.com",
"_links": {
"self": { "href": "/users/1234" },
"orders": { "href": "/users/1234/orders" },
"update": { "href": "/users/1234", "method": "PUT" },
"delete": { "href": "/users/1234", "method": "DELETE" }
}
}
At this level, the API becomes self-documenting and more resilient to changes, as clients rely on hypermedia controls rather than hardcoded endpoints.
Most APIs that call themselves "RESTful" reach Level 2, while true REST as defined by Roy Fielding requires Level 3. However, each level provides benefits, and the right choice depends on your specific needs.
Practice Activities
Activity 1: REST Resource Identification
Identify appropriate resource names, HTTP methods, and URLs for the following operations in a bookstore API:
- Get a list of all books
- Get details of a specific book
- Add a new book
- Update a book's information
- Delete a book
- Get all reviews for a specific book
- Add a review to a book
- Get all books by a specific author
Activity 2: REST Anti-Pattern Detection
Identify the REST anti-patterns in the following API endpoints and suggest RESTful alternatives:
- POST /api/deleteUser/1234
- GET /api/users/getAll
- POST /api/search?query=programming
- GET /api/updateProduct/5678?name=NewName&price=99.99
- POST /api/orders/cancel/9012
Activity 3: HATEOAS Implementation
Design a HATEOAS-compliant response for an order resource in an e-commerce API. The response should include links for:
- Getting order details
- Getting the customer who placed the order
- Getting the order items
- Canceling the order (if it's in a cancelable state)
- Paying for the order (if it's unpaid)
- Tracking the shipment (if it's shipped)
Implement this using both a simple "_links" approach and using the JSON:API format.
Summary
In this lecture, we've explored the foundational principles of REST architecture:
- REST as an architectural style for networked applications
- The six constraints: Client-Server, Statelessness, Cacheability, Uniform Interface, Layered System, and Code on Demand
- Resources as the central concept, with representations in various formats
- Using HTTP methods, status codes, and headers effectively
- The importance of HATEOAS for truly RESTful services
- Comparing REST with other architectural styles like SOAP, GraphQL, and gRPC
- Common REST anti-patterns to avoid
- The Richardson Maturity Model for evaluating REST implementations
Understanding these principles is crucial for designing effective, scalable, and maintainable web APIs. In the next lectures, we'll explore practical aspects of API design and implementation based on these RESTful principles.
Further Reading
- Roy Fielding's Dissertation - The original definition of REST
- Richardson Maturity Model - Martin Fowler's explanation of the REST maturity model
- REST APIs must be hypertext-driven - Roy Fielding's clarification of REST requirements
- JSON:API Specification - A standardized way to build REST APIs
- REST API Design Rulebook - Practical guidelines for RESTful API design