Implementing HATEOAS
HATEOAS: clients navigate by following links discovered in responses, not hardcoded URIs. Highest level (3) of Richardson Maturity Model.
| Aspect | HATEOAS | Without |
| URI knowledge | Discovered from responses | Hardcoded in client |
| State transitions | Server tells client what's possible | Client guesses based on state |
| Coupling | Loose | Tight |
2. Including Resource Links
Example: Inline Links
{
"id": 100,
"status": "pending",
"links": [
{"rel": "self", "href": "/orders/100", "method": "GET"},
{"rel": "cancel", "href": "/orders/100/cancel", "method": "POST"},
{"rel": "pay", "href": "/orders/100/payments", "method": "POST"}
]
}
Hypertext Application Language — minimal hypermedia format.
| Field | Purpose |
_links | Object of link relations |
_embedded | Embedded related resources |
| Content-Type | application/hal+json |
Example: HAL Response
{
"id": 100,
"amount": 250.00,
"_links": {
"self": {"href": "/orders/100"},
"user": {"href": "/users/42"}
},
"_embedded": {
"items": [
{"sku": "ABC", "qty": 2, "_links": {"self": {"href": "/products/ABC"}}}
]
}
}
4. Using JSON:API Specification
| Element | Purpose |
data | Primary resource(s) |
included | Sideloaded relations |
links | Pagination, self |
relationships | Resource linkage |
| Content-Type | application/vnd.api+json |
5. Providing Navigation Links
| rel | Use |
self | Current resource URI |
next / prev | Pagination |
first / last | Boundaries |
up | Parent resource |
related | Related resource |
6. Including Action Links
| State | Available Actions |
| order.status = pending | cancel, pay, edit |
| order.status = paid | refund, ship |
| order.status = shipped | track, return |
7. Using Link Relations
| Source | Examples |
| IANA registry | self, next, edit, collection |
| Custom (URI) | https://api.example.com/rels/cancel |
| Compact (CURIE) | ex:cancel with curies map |
8. Implementing Resource Discovery
Example: API Root Document
{
"_links": {
"self": {"href": "/"},
"users": {"href": "/users"},
"orders": {"href": "/orders"},
"products": {"href": "/products"},
"search": {"href": "/search?q={query}", "templated": true}
}
}
| Header | Format (RFC 8288) |
| Single link | Link: </users/42>; rel="self" |
| Multiple | Comma-separated |
| With type | Link: <...>; rel="next"; type="application/json" |
10. Balancing HATEOAS vs Simplicity
| Use HATEOAS When | Skip When |
| Public API, many clients | Single internal client |
| State machines (workflows) | Simple CRUD |
| Long evolution lifecycle | Rapid iteration |
| Discoverability matters | OpenAPI doc sufficient |