Should REST APIs Implement HATEOAS Hypermedia Links?
TL;DR
HATEOAS (Hypermedia as the Engine of Application State) is theoretically elegant but practically complex. Most APIs skip full HATEOAS and use selective hypermedia links for pagination, related resources, and actions. Modern PetstoreAPI implements practical hypermedia links without forcing clients to be fully hypermedia-driven.
Introduction
You’re reading about REST API design. You encounter HATEOAS (Hypermedia as the Engine of Application State). The explanation says: “Clients should discover all actions through hypermedia links, not hardcode URLs.”
You think: “That sounds complicated. Does anyone actually do this?”
The answer: not really. HATEOAS is the most skipped REST constraint. Roy Fielding (who invented REST) says it’s essential. Most API designers say it’s impractical. The result: most “REST” APIs aren’t truly RESTful according to Fielding’s definition.
Modern PetstoreAPI takes a pragmatic approach: use hypermedia links where they add value (pagination, related resources, actions) but don’t force clients to be fully hypermedia-driven.
In this guide, you’ll learn what HATEOAS is, why it’s controversial, and how to implement practical hypermedia links using Modern PetstoreAPI as a reference.
What Is HATEOAS?
HATEOAS is a REST constraint that says clients should discover API capabilities through hypermedia links, not documentation.
The Concept
Instead of hardcoding URLs:
// Client hardcodes URLs
const response = await fetch('https://petstoreapi.com/v1/pets/123');
const pet = await response.json();
// Client knows the URL structure
await fetch(`https://petstoreapi.com/v1/pets/${pet.id}/orders`);
Clients follow links from responses:
// Client starts at root
const root = await fetch('https://petstoreapi.com/v1');
const rootData = await root.json();
// Client follows link to pets
const petsUrl = rootData._links.pets.href;
const pets = await fetch(petsUrl);
const petsData = await pets.json();
// Client follows link to specific pet
const petUrl = petsData._links.self.href;
const pet = await fetch(petUrl);
const petData = await pet.json();
// Client follows link to orders
const ordersUrl = petData._links.orders.href;
const orders = await fetch(ordersUrl);
The Theory
With HATEOAS:
1. Clients don’t hardcode URLs
URLs can change without breaking clients. The server controls the URL structure.
2. Clients discover capabilities
If a link exists, the action is available. If not, it’s not available (or not allowed for this user).
3. APIs are self-documenting
Clients explore the API by following links, like browsing a website.
Example: Full HATEOAS Response
{
"id": "019b4132-70aa-764f-b315-e2803d882a24",
"name": "Fluffy",
"species": "CAT",
"status": "AVAILABLE",
"_links": {
"self": {
"href": "https://petstoreapi.com/v1/pets/019b4132-70aa-764f-b315-e2803d882a24"
},
"update": {
"href": "https://petstoreapi.com/v1/pets/019b4132-70aa-764f-b315-e2803d882a24",
"method": "PUT"
},
"delete": {
"href": "https://petstoreapi.com/v1/pets/019b4132-70aa-764f-b315-e2803d882a24",
"method": "DELETE"
},
"orders": {
"href": "https://petstoreapi.com/v1/pets/019b4132-70aa-764f-b315-e2803d882a24/orders"
},
"adopt": {
"href": "https://petstoreapi.com/v1/pets/019b4132-70aa-764f-b315-e2803d882a24/adopt",
"method": "POST"
}
}
}
The client doesn’t need to know URL patterns. It follows links.
The HATEOAS Debate
HATEOAS is controversial because theory and practice diverge.
Arguments For HATEOAS
1. Loose coupling
Clients don’t depend on URL structure. The server can change URLs without breaking clients.
2. Discoverability
Clients can explore the API without reading documentation.
3. State-driven actions
Links show what actions are available. If a pet is adopted, the “adopt” link disappears.
4. True REST
Roy Fielding says HATEOAS is essential to REST. Without it, you’re not doing REST.
Arguments Against HATEOAS
1. Complexity
Clients need hypermedia parsing logic. Simple HTTP clients become complex state machines.
2. Performance
Clients make multiple requests to discover URLs. Direct URL access is faster.
3. Debugging difficulty
Following links makes debugging harder. You can’t just curl a URL—you need to follow the link chain.
4. Poor tooling
Most HTTP clients, testing tools, and documentation generators assume hardcoded URLs.
5. Nobody does it
GitHub, Stripe, Twilio, Twitter—major APIs don’t use full HATEOAS. If they don’t need it, do you?
The Reality
Most APIs claim to be “REST” but skip HATEOAS. They’re actually “HTTP APIs” or “REST-like APIs.” True REST (with HATEOAS) is rare.
Practical Hypermedia Links
Instead of full HATEOAS, use hypermedia links where they add value.
1. Pagination Links
Problem: Clients need to construct pagination URLs.
Solution: Provide next/prev links.
{
"data": [...],
"pagination": {
"page": 2,
"limit": 20,
"totalPages": 10
},
"links": {
"self": "https://petstoreapi.com/v1/pets?page=2&limit=20",
"first": "https://petstoreapi.com/v1/pets?page=1&limit=20",
"prev": "https://petstoreapi.com/v1/pets?page=1&limit=20",
"next": "https://petstoreapi.com/v1/pets?page=3&limit=20",
"last": "https://petstoreapi.com/v1/pets?page=10&limit=20"
}
}
Benefit: Clients don’t construct pagination URLs. They follow links.
2. Related Resource Links
Problem: Clients need to know URL patterns for related resources.
Solution: Provide links to related resources.
{
"id": "019b4132-70aa-764f-b315-e2803d882a24",
"name": "Fluffy",
"_links": {
"self": "https://petstoreapi.com/v1/pets/019b4132-70aa-764f-b315-e2803d882a24",
"orders": "https://petstoreapi.com/v1/pets/019b4132-70aa-764f-b315-e2803d882a24/orders",
"owner": "https://petstoreapi.com/v1/users/019b4127-54d5-76d9-b626-0d4c7bfce5b6"
}
}
Benefit: Clients discover related resources without documentation.
3. Action Links
Problem: Clients need to know which actions are available.
Solution: Provide links for available actions.
{
"id": "019b4132-70aa-764f-b315-e2803d882a24",
"status": "AVAILABLE",
"_links": {
"adopt": {
"href": "https://petstoreapi.com/v1/pets/019b4132-70aa-764f-b315-e2803d882a24/adopt",
"method": "POST"
}
}
}
If the pet is already adopted:
{
"id": "019b4132-70aa-764f-b315-e2803d882a24",
"status": "ADOPTED",
"_links": {
// No "adopt" link - action not available
}
}
Benefit: Links indicate available actions based on state.
4. Cursor-Based Pagination
Problem: Clients need to construct cursor URLs.
Solution: Provide opaque next/prev URLs.
{
"data": [...],
"links": {
"next": "https://petstoreapi.com/v1/pets?cursor=eyJpZCI6IjAxOWI0MTMyIn0"
}
}
Benefit: Clients don’t parse cursors. They follow links.
How Modern PetstoreAPI Uses Hypermedia
Modern PetstoreAPI uses selective hypermedia links.
Pagination Links
All collection endpoints include pagination links:
GET /v1/pets?limit=20
{
"data": [...],
"pagination": {
"limit": 20,
"hasMore": true
},
"links": {
"self": "https://petstoreapi.com/v1/pets?limit=20",
"next": "https://petstoreapi.com/v1/pets?cursor=eyJpZCI6IjAxOWI0MTMyIn0&limit=20"
}
}
Related Resource Links
Resource responses include links to related resources:
GET /v1/pets/019b4132-70aa-764f-b315-e2803d882a24
{
"id": "019b4132-70aa-764f-b315-e2803d882a24",
"name": "Fluffy",
"ownerId": "019b4127-54d5-76d9-b626-0d4c7bfce5b6",
"_links": {
"self": "https://petstoreapi.com/v1/pets/019b4132-70aa-764f-b315-e2803d882a24",
"owner": "https://petstoreapi.com/v1/users/019b4127-54d5-76d9-b626-0d4c7bfce5b6",
"orders": "https://petstoreapi.com/v1/pets/019b4132-70aa-764f-b315-e2803d882a24/orders"
}
}
No Full HATEOAS
Modern PetstoreAPI doesn’t require clients to be hypermedia-driven. Clients can:
Option 1: Follow links (hypermedia-driven)
const pet = await fetch(petUrl);
const ownerUrl = pet._links.owner.href;
const owner = await fetch(ownerUrl);
Option 2: Construct URLs (traditional)
const pet = await fetch(`https://petstoreapi.com/v1/pets/${petId}`);
const owner = await fetch(`https://petstoreapi.com/v1/users/${pet.ownerId}`);
Both approaches work. Links are provided for convenience, not enforcement.
Testing Hypermedia APIs with Apidog
Apidog helps you test hypermedia links and validate link correctness.
Test Link Presence
Verify that responses include expected links:
// Apidog test script
pm.test("Response includes pagination links", () => {
const links = pm.response.json().links;
pm.expect(links).to.have.property('self');
pm.expect(links).to.have.property('next');
});
Test Link Validity
Follow links and verify they work:
// Apidog test script
const nextUrl = pm.response.json().links.next;
pm.sendRequest(nextUrl, (err, response) => {
pm.test("Next link returns 200", () => {
pm.expect(response.code).to.equal(200);
});
});
Test Link Format
Verify links follow expected format:
pm.test("Links are absolute URLs", () => {
const links = pm.response.json().links;
Object.values(links).forEach(link => {
pm.expect(link).to.match(/^https:\/\//);
});
});
When to Use HATEOAS
Use hypermedia links when they add value. Skip them when they don’t.
Use Hypermedia Links For:
1. Pagination - Clients shouldn’t construct pagination URLs
2. Related resources - Convenient navigation
3. State-dependent actions - Show available actions based on resource state
4. Complex workflows - Guide clients through multi-step processes
Skip HATEOAS For:
1. Simple CRUD APIs - Clients can construct URLs easily
2. Internal APIs - Teams can coordinate URL changes
3. Performance-critical APIs - Extra links add response size
4. Mobile APIs - Bandwidth matters, links add overhead
Conclusion
HATEOAS is theoretically elegant but practically complex. Most APIs skip full HATEOAS and use selective hypermedia links where they add value.
Modern PetstoreAPI demonstrates practical hypermedia: pagination links, related resource links, and action links—without forcing clients to be fully hypermedia-driven.
Use Apidog to test hypermedia links, validate link correctness, and ensure your API provides useful navigation.
Key takeaways:
- Full HATEOAS is rare and complex
- Selective hypermedia links add value without complexity
- Pagination links are the most useful hypermedia feature
- Don’t force clients to be hypermedia-driven
- Test links to ensure they work correctly
Explore Modern PetstoreAPI documentation to see practical hypermedia implementation.
FAQ
Is HATEOAS required for REST APIs?
According to Roy Fielding (REST inventor), yes. In practice, no. Most “REST” APIs skip HATEOAS and are technically “HTTP APIs” or “REST-like APIs.”
What does HATEOAS stand for?
Hypermedia as the Engine of Application State. It means clients discover API capabilities through hypermedia links, not hardcoded URLs.
Do major APIs use HATEOAS?
No. GitHub, Stripe, Twilio, and most major APIs don’t use full HATEOAS. They may include some hypermedia links (pagination, related resources) but don’t require clients to be hypermedia-driven.
What’s the difference between HATEOAS and hypermedia links?
HATEOAS is a constraint requiring clients to be fully hypermedia-driven. Hypermedia links are just links in responses. You can include links without enforcing HATEOAS.
Should I implement HATEOAS in my API?
Probably not full HATEOAS. Use selective hypermedia links for pagination and related resources. Don’t force clients to be hypermedia-driven unless you have a specific reason.
How do I test HATEOAS APIs?
Use Apidog to verify link presence, follow links, and validate link correctness. Test that links return expected responses.
What’s the HAL format?
HAL (Hypertext Application Language) is a standard format for hypermedia links. It uses _links and _embedded fields. Modern PetstoreAPI uses HAL-inspired link format.
Can clients ignore hypermedia links?
Yes. If your API provides links but doesn’t require clients to use them, clients can construct URLs directly. This is the pragmatic approach most APIs take.



