Most developers claim their APIs are “RESTful,” but are they really? Leonard Richardson introduced a maturity model that classifies APIs into four levels, from Level 0 (basically RPC over HTTP) to Level 3 (true REST with hypermedia). In this article, we’ll explore each level and implement them in FastAPI.
What is the Richardson Maturity Model?
The Richardson Maturity Model, popularized by Martin Fowler, provides a way to grade your API according to how well it adheres to REST principles. Think of it as a ladder toward REST enlightenment.
Level 3: Hypermedia Controls (HATEOAS) |
Let’s explore each level with a practical example: a doctor appointment booking system.
Level 0: The Swamp of POX (Plain Old XML/JSON)
At Level 0, HTTP is merely a transport mechanism. Everything goes to a single endpoint, and the operation is specified in the request body.
Characteristics
- Single endpoint for all operations
- Operation type embedded in request body
- HTTP used only as a tunnel
- Essentially RPC over HTTP
FastAPI Example
from fastapi import FastAPI |
Usage
# Get slots |
Problems with Level 0
- No use of HTTP semantics
- Everything looks the same to intermediaries (caching impossible)
- Error handling is custom and inconsistent
- No discoverability
Level 1: Resources
Level 1 introduces the concept of resources. Instead of one endpoint, we have multiple endpoints representing different entities.
Characteristics
- Multiple endpoints (resources)
- Each resource has its own URI
- Still primarily uses POST for everything
- Resources are nouns, not verbs
FastAPI Example
from fastapi import FastAPI |
Improvement Over Level 0
- Resources are identifiable by URI
/doctors/dr_jonesis clearly different from/slots/slot_1- Better organization and clarity
Remaining Issues
- Still using POST for read operations
- HTTP verbs not utilized properly
- Caching still difficult
Level 2: HTTP Verbs
Level 2 properly leverages HTTP methods (GET, POST, PUT, PATCH, DELETE) and status codes.
Characteristics
- GET for safe, read-only operations
- POST for creating resources
- PUT/PATCH for updating resources
- DELETE for removing resources
- Proper HTTP status codes (201, 404, 409, etc.)
- Enables caching for GET requests
FastAPI Example
from fastapi import FastAPI, HTTPException, status |
HTTP Methods and Their Semantics
| Method | Safe | Idempotent | Cacheable | Use Case |
|---|---|---|---|---|
| GET | Yes | Yes | Yes | Retrieve resources |
| POST | No | No | No | Create resources |
| PUT | No | Yes | No | Replace resources |
| PATCH | No | No | No | Partial update |
| DELETE | No | Yes | No | Remove resources |
Status Codes Matter
# Good: Semantic status codes |
Level 3: Hypermedia Controls (HATEOAS)
HATEOAS (Hypermedia As The Engine Of Application State) is the pinnacle of REST. Responses include links that tell clients what actions are possible next.
Characteristics
- Self-documenting API responses
- Clients discover actions dynamically
- Server can change URLs without breaking clients
- API behaves like a state machine
FastAPI Example
from fastapi import FastAPI, HTTPException, Request, status |
Example Response
When you book a slot, you get not just the appointment data but also links to possible next actions:
{ |
The client now knows:
- How to view the appointment (
self) - How to cancel it (
cancel) - How to reschedule it (
reschedule) - How to add lab tests (
add_tests)
Advanced HATEOAS with FastAPI-HATEOAS
For production use, consider using a library. Here’s a more structured approach:
from fastapi import FastAPI, Request |
Comparison Summary
| Aspect | Level 0 | Level 1 | Level 2 | Level 3 |
|---|---|---|---|---|
| Endpoints | Single | Multiple | Multiple | Multiple |
| HTTP Verbs | POST only | POST only | GET/POST/PUT/DELETE | GET/POST/PUT/DELETE |
| Status Codes | 200 only | 200 only | Semantic | Semantic |
| Caching | No | No | Yes (GET) | Yes (GET) |
| Discoverability | No | No | No | Yes |
| Client Coupling | High | Medium | Medium | Low |
When to Use Each Level
Level 2 is Often Enough
For most APIs, Level 2 (proper HTTP verbs and status codes) is sufficient. It provides:
- Clear semantics
- Caching support
- Standard error handling
- Good developer experience
Consider Level 3 When
- Building public APIs with long lifecycles
- API consumers need to adapt to changes automatically
- You want truly decoupled client-server evolution
- Building hypermedia-driven applications
Practical Considerations
# Level 2 is clean and practical |
Conclusion
The Richardson Maturity Model provides a useful framework for evaluating and improving REST APIs:
- Level 0: HTTP as transport (avoid this)
- Level 1: Resources with URIs (basic structure)
- Level 2: HTTP verbs and status codes (recommended baseline)
- Level 3: HATEOAS for self-documenting APIs (when needed)
Most FastAPI applications should aim for Level 2 at minimum. Level 3 (HATEOAS) adds value for complex, long-lived APIs where client-server decoupling is critical.
Remember: REST is not about which level you’re at, but about choosing the right level for your use case. A well-designed Level 2 API is better than a poorly implemented Level 3 one.