What is HATEOAS?
Without HATEOAS:
Junior Level
HATEOAS (Hypermedia As The Engine Of Application State) is a REST principle where the server returns not only data but also links to possible actions.
Simple example:
Without HATEOAS:
{
"id": 101,
"status": "NEW"
}
With HATEOAS:
{
"id": 101,
"status": "NEW",
"_links": {
"self": { "href": "/orders/101" },
"payment": { "href": "/orders/101/pay" },
"cancel": { "href": "/orders/101/cancel" }
}
}
Why is this needed?
- The client doesn’t need to “know” URLs — it just follows the links
- If the server changes URLs, the client won’t break
- The API becomes self-documenting
Analogy:
It’s like a website: you don’t know all the URLs by heart, you just click on the links on the page.
Middle Level
The Concept of Affordances
In HATEOAS, links are not just URLs — they are Affordances:
- Affordance (in the context of HATEOAS) — a hint from the server to the client about which actions are currently possible. Like a button on a web page “suggests” that it can be clicked. If the server didn’t return a
rel="delete"link — it means deletion is currently not possible. - If the user doesn’t have deletion permissions — the server simply won’t send the
rel="delete"link - The client doesn’t need to duplicate business logic for permission checks
- Enough:
if (response.links.delete) showDeleteButton()
HAL Standard (Hypertext Application Language)
The most popular format. Uses the _links field:
{
"id": 101,
"status": "NEW",
"_links": {
"self": { "href": "/orders/101" },
"payment": { "href": "/orders/101/pay" },
"cancel": { "href": "/orders/101/cancel" }
}
}
Spring HATEOAS
The library uses RepresentationModel and LinkDiscoverer:
WebMvcLinkBuilderbuilds links dynamically by reading controller mappings- Problem: Behind a proxy (Nginx/API Gateway), links may contain an internal IP
- Solution:
X-Forwarded-*headers andForwardedHeaderFilterconfiguration
Performance
- Payload Overhead: For a single resource, overhead is 30-50%. For collections of 100+ items — up to 2-3x, since each entry contains its own links.
- Optimization: Links only in the detailed view (Single Resource), in lists — only
self - CPU Cost: Generating links for 1000 items requires resources (reflection in Spring)
Edge Cases
- Versioning: If you changed the payment endpoint path, a client using HATEOAS won’t notice — it takes the URL from the link. This provides perfect backward compatibility
- Client Caching: Caching responses with HATEOAS can be dangerous if links depend on user permissions. If HATEOAS responses are cached and links depend on user permissions, add
Vary: Authorization, otherwise the CDN may serve a cached response with someone else’s links.
Senior Level
When NOT to use HATEOAS
- Internal microservices — overhead without benefit
- Mobile apps with fixed screens — URLs don’t change
- Highload systems — where every byte counts
- Client and backend are the same team — coupling is already minimal
HATEOAS as State Machine
HATEOAS implements the State Machine pattern at the network protocol level:
- The client is a “dumb” executor that follows links
- The server manages application state through available links
- This radically reduces coupling between frontend and backend
Spring HATEOAS (Under the hood)
RepresentationModel— base class for resources with linksLinkDiscoverer— parses and finds links in responses- Proxy problem:
WebMvcLinkBuilderbuilds links based on internal mappings. Behind Nginx/API Gateway, links contain an internal IP
Performance and Highload
Payload Overhead
In Highload systems, HATEOAS can be a problem:
- For a single resource, overhead is 30-50%. For collections of 100+ items — up to 2-3x
- CPU cost for generating links for each entry (reflection)
Optimizations
- Links only in Single Resource, in Collections — only
self - Compact link formats (minimal rel names)
- Caching link templates
Diagnostics and Monitoring
- Discoverability: You can “walk” through the API via HAL Browser, clicking on links
- Broken Links: Monitoring should check the validity of generated links. If
selfpoints to 404 — it’s a generator bug - Vary Header: If HATEOAS responses are cached and links depend on user permissions, add
Vary: Authorization, otherwise the CDN may serve a cached response with someone else’s links.
Interview Cheat Sheet
Must know:
- HATEOAS = Hypermedia As The Engine Of Application State — server returns data + links to actions
- Client doesn’t need to know URLs — it follows links from server responses
- Affordances — server hints about available actions; no
rel="delete"link = deletion not possible - HAL standard uses
_linksfield withself,payment,cancel, etc. - Payload Overhead: 30-50% for a single resource, up to 2-3x for collections of 100+ items
- HATEOAS is not needed for: internal microservices, mobile apps, highload systems
- Spring HATEOAS:
RepresentationModel+WebMvcLinkBuilder; proxy problem solved viaX-Forwarded-*
Common follow-up questions:
- What is Affordance in the context of HATEOAS? — Server hint about available actions (like a button on a page)
- Why does HATEOAS increase JSON size? — Each entry contains links; for collections overhead is up to 2-3x
- How to solve the proxy links problem? — X-Forwarded-* headers and ForwardedHeaderFilter configuration in Spring
- When is HATEOAS definitely not needed? — Internal microservices, mobile with fixed screens, highload
Red flags (DO NOT say):
- “HATEOAS is needed in every project” — excessive for internal APIs and mobile
- “HATEOAS is just links in JSON” — it’s also a State Machine and coupling management
- “HATEOAS links can be cached without Vary: Authorization” — CDN will serve someone else’s links to another user
- “HATEOAS reduces response size” — it increases it by 30-50% for a single resource
Related topics:
- [[What is REST]]
- [[What is RESTful API design]]
- [[Should you use verbs in URL]]
- [[How to organize REST API versioning]]
- [[What is Accept header]]