Dynamic Discovery in HATEOAS — How Clients Navigate APIs at Runtime
In this tutorial, you will learn about Dynamic Discovery in HATEOAS. We cover key concepts, practical examples, and best practices to help you master this topic.
Dynamic discovery is the core benefit of HATEOAS, where clients start at a root entry point and discover available resources and actions by following links, without any hardcoded URLs.
What You'll Learn
- How clients discover API capabilities at runtime
- The role of the root entry point in discovery
- Implementing generic hypermedia clients
Why It Matters
Dynamic discovery enables generic API clients that work with any HATEOAS-compliant API. This is the vision of truly RESTful systems — clients and servers evolving independently.
flowchart LR
A["GET /api"] --> B["Entry Point\nResponse with links"]
B --> C["GET /users"]
B --> D["GET /orders"]
B --> E["GET /products"]
C --> F["User's links to\norders, profile, edit"]
D --> G["Order's links to\nitems, payments, cancel"]
style B fill:#dbeafe,stroke:#2563eb
Code Examples
# Generic HATEOAS client
class HypermediaClient:
def __init__(self, entry_url):
self.session = requests.Session()
self.entry = self.session.get(entry_url).json()
def follow(self, rel, method="GET", data=None):
link = self.entry.get("_links", {}).get(rel)
if not link:
raise KeyError(f"No link with rel '{rel}'")
url = f"{self.session.headers.get('base', '')}{link['href']}"
return self.session.request(method, url, json=data)
client = HypermediaClient("https://api.example.com/api")
users = client.follow("users").json()
order_link = users[0]["_links"]["orders"]
# Client discovers next steps dynamically
// JavaScript hypermedia client
class HypermediaClient {
constructor(baseUrl) {
this.baseUrl = baseUrl;
}
async discover() {
this.root = await this.fetchRel('self');
}
async fetchRel(rel) {
if (!this.current?._links?.[rel]) {
throw new Error(`No ${rel} link`);
}
const link = this.current._links[rel];
const res = await fetch(`${this.baseUrl}${link.href}`);
return res.json();
}
}
Common Mistakes
1. Having No Entry Point
Every HATEOAS API should have a root resource that links to all major collections.
2. Hardcoding URLs in Generic Clients
A truly generic client never constructs URLs. It always follows links.
3. Not Including a self Link at the Root
The root response should link to itself for discovery bootstrapping.
4. Clients Caching Links Indefinitely
Links can change. Clients should re-fetch resources to get current links.
5. Ignoring Link Relations You Don't Recognize
Generic clients should gracefully skip unknown rels.
Practice Questions
- What is the role of the entry point in HATEOAS?
- How does a client discover available actions without documentation?
- What happens if a client follows an outdated link?
- Why should clients avoid caching links permanently?
- Can a generic HATEOAS client work with any API?
Answers:
- The entry point is the root URL that links to all primary resources.
- By reading
_linksand_actionsfrom API responses. - It may get a 404 or redirected to the current URL.
- Resource states change, and available links change with them.
- Yes, as long as the API consistently follows HATEOAS conventions.
Challenge: Build a minimal generic hypermedia client that can navigate any HATEOAS API. Start with an entry point and discover resources through links only.
FAQ
What's Next
Build a HATEOAS Client in your language of choice, then compare HATEOAS vs GraphQL.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro