HATEOAS: REST Level 3 API Design & HAL Implementation
HATEOAS is the highest level of REST maturity — your API responses include links to every action a client can take next. Clients navigate the API like a website, following hyperlinks rather than hardcoding paths. This guide covers the theory and practical HAL implementation.
Richardson Maturity Model — Levels 0 to 3
| Level | Name | Description | Example |
|---|---|---|---|
| 0 | Swamp of POX | Single endpoint, all operations via POST | POST /api {action: "getUser"} |
| 1 | Resources | Multiple resource URLs, still mostly POST | POST /users/42 |
| 2 | HTTP Verbs | Correct verbs + status codes per operation | GET /users/42 → 200 |
| 3 | Hypermedia | Responses include _links to available actions | {"_links": {"self": ...}} |
HAL+JSON Response Format
HAL uses _links for hypermedia controls and _embedded for related resources. The Content-Type is application/hal+json:
// GET /orders/42
// Content-Type: application/hal+json
{
"id": 42,
"total": 149.99,
"status": "pending",
"currency": "USD",
"_links": {
"self": { "href": "/orders/42" },
"cancel": { "href": "/orders/42/cancel", "title": "Cancel this order" },
"payment": { "href": "/orders/42/payment" },
"collection": { "href": "/orders" },
"customer": { "href": "/customers/7" }
},
"_embedded": {
"items": [
{
"id": 101,
"name": "Developer Playbook Pro",
"quantity": 1,
"price": 149.99,
"_links": {
"product": { "href": "/products/101" }
}
}
]
}
} Node.js HAL Builder
A minimal helper that attaches _links to any response object and sets the correct Content-Type:
// utils/hal.ts
type Link = { href: string; title?: string; templated?: boolean };
type Links = Record<string, Link | Link[]>;
export function hal<T extends object>(
resource: T,
links: Links,
embedded?: Record<string, object | object[]>,
): T & { _links: Links; _embedded?: Record<string, unknown> } {
return {
...resource,
_links: links,
...(embedded ? { _embedded: embedded } : {}),
};
}
// In a route handler:
app.get('/orders/:id', async (req, res) => {
const order = await db.orders.findById(req.params.id);
const canCancel = order.status === 'pending';
const links: Links = {
self: { href: `/orders/${order.id}` },
collection: { href: '/orders' },
customer: { href: `/customers/${order.customerId}` },
};
if (canCancel) {
links.cancel = { href: `/orders/${order.id}/cancel`, title: 'Cancel order' };
}
res.contentType('application/hal+json').json(hal(order, links));
}); HAL vs Siren vs JSON:API Hypermedia
| Feature | HAL | Siren | JSON:API |
|---|---|---|---|
| Links | _links object | links array | links object |
| Actions (forms) | Not included | actions array | Not included |
| Embedded | _embedded | entities | included |
| Adoption | Widespread | Niche | Widespread |
| Content-Type | application/hal+json | application/vnd.siren+json | application/vnd.api+json |
Related Products
on GumroadDevPlaybook Interview Vault
210+ real technical interview questions with model answers, asked at Google, Meta, Amazon, Microsoft, Stripe, and Shopify. Covers Frontend (55), Backend (55), System Design (50), and DSA (50). Available in English and Traditional Chinese.
Buy on Gumroad →TypeScript Quick Reference Toolkit
5 practical reference files for TypeScript developers: cheatsheet, TS vs JS comparison, 20 utility types with real examples, tsconfig best practices template, and 15 common mistakes guide. Print it. Keep it on your desk.
Buy on Gumroad →Developer Interview Vault
210 technical interview questions with model answers, key points, and follow-up questions. Covers Frontend, Backend, System Design, and DSA — bilingual English + Traditional Chinese (ZH-TW). PWYW from $29.
Buy on Gumroad →You Might Also Need
OpenAPI Validator
Paste your OpenAPI 3.x spec (YAML or JSON) to validate structure, find missing required fields, detect duplicate paths, check for missing operationIds, and get errors and warnings with path references.
Open free tool →
API Response Mock Generator
Generate realistic mock API responses with custom status codes, headers, and JSON body. Quick templates for 200, 401, 404, 429, 500 and more. Runs in your browser.
Open free tool →
API Response Time Tester
Test API endpoint response times directly from your browser. Run multiple requests, see average/min/max times, status codes, and response sizes. GET, POST, PUT, DELETE support. Free.
Open free tool →
More Free Tools
Related Articles
GraphQL vs REST vs gRPC: A 2026 API Design Comparison
A practical guide comparing GraphQL vs REST vs gRPC for modern API development. Covers performance, type safety, tooling, use cases, and a decision framework for 2026.
Complete HTTP Status Codes Reference for REST API Devs
The complete HTTP status codes reference for REST API developers. Every 1xx, 2xx, 3xx, 4xx, and 5xx code explained with real-world API examples and best practices.
REST API Best Practices: Endpoint Naming, Versioning, and Error Handling
Design clean, consistent REST APIs with proper URL naming, HTTP methods, versioning, error formats, pagination, auth, and rate limiting.
Understanding REST API Design Best Practices
A practical guide to designing REST APIs that developers love — covering resource modeling, HTTP semantics, status codes, versioning, and error handling.
The Ultimate API Design Checklist for Production Apps
Ship production-ready APIs with this comprehensive checklist. Covers REST design, authentication, validation, error handling, rate limiting, versioning, documentation, and monitoring.
Enjoying this tool? Get more like it.
Weekly developer tool guides, productivity playbooks & AI tricks. Free. No spam.
Unlock AI-Powered Dev Tools
- ⚡ AI Code Review, Doc Generator & SQL Builder
- ⚡ All premium templates & early access
- ⚡ Member discounts on Gumroad products
$9/mo after trial · Cancel anytime
Want the full toolkit?
Get DevPlaybook Pro
Every template, guide, boilerplate, and automation script in one bundle. 13 premium products — grab them all at once and save big.
MIT licensed · Instant download · No subscription
See what's included → Browse all deals →Frequently Asked Questions
What is HATEOAS and why does it matter?▼
HATEOAS (Hypermedia As The Engine Of Application State) is a constraint of REST that says responses should include links to related actions the client can take next. Instead of hardcoding URLs in the client, the client discovers them from the server's responses. This decouples the client from the URL structure and lets the server evolve its API without breaking clients that follow links.
What is the Richardson Maturity Model?▼
The Richardson Maturity Model (RMM) describes four levels of REST API maturity. Level 0: Single endpoint, RPC-style (SOAP, XML-RPC). Level 1: Multiple resources, each with their own URL. Level 2: HTTP verbs used correctly (GET for reads, POST for creates, DELETE for deletes, appropriate status codes). Level 3: Hypermedia controls — responses include links to available actions. Most APIs reach Level 2; true Level 3 HATEOAS is rare but powerful for complex workflows.
What is HAL (Hypertext Application Language)?▼
HAL is a lightweight convention for embedding hypermedia links in JSON responses. It uses two reserved properties: _links (an object of link relations to URL objects with href) and _embedded (for sideloaded related resources). HAL is simple to implement and widely understood. It is defined in IETF draft RFC draft-kelly-json-hal and has library support in Spring, Node.js, and .NET.
What link relation types should I use?▼
Use IANA-registered link relation types when available: self (canonical URL of this resource), next/prev (pagination), first/last (pagination bounds), related (related resource), item (item within a collection), collection (parent collection). For domain-specific relations, use a URI as the relation type (e.g. https://api.example.com/rels/cancel). Avoid inventing short non-URI relation names as they can clash.
Is HATEOAS practical for modern SPAs and mobile apps?▼
HATEOAS has practical challenges for tightly-typed frontends. SPA and mobile clients often need to know the URL structure at build time for navigation and deep linking. Many teams implement a partial HATEOAS approach: include self links and pagination links (which clients actually use), but stop short of encoding all workflow transitions as links. The key benefit — clients following links rather than hardcoding paths — is most valuable for third-party API consumers.
What is the difference between HAL and Siren?▼
HAL focuses on links and embedded resources — it represents state and navigation. Siren extends this with actions: structured descriptions of forms/mutations (method, fields, type) that clients can use to perform writes without knowing the URL or HTTP method upfront. Siren is more expressive but less widely adopted. HAL is the safer default for most APIs; Siren suits complex workflow-driven APIs.