They act as data-transfer services, but they lack the defining constraint that makes REST, well, REST: HATEOAS (Hypermedia as the Engine of Application State).
What Does HATEOAS Really Mean?
Fielding’s vision for REST was not about exposing database rows as URLs; it was about creating a system that acts like the web itself.
When you browse the web, you don't hardcode URLs into your browser's address bar to navigate a site. You read a page, see a link, and click it. You submit a form to change your state. The information on the screen provides the affordances—the cues—that tell you what you are allowed to do next.
As Fielding famously noted:
"When I say hypertext, I mean the simultaneous presentation of information and controls such that the information becomes the affordance through which the user (or automaton) obtains choices and selects actions."
For an API to be truly RESTful, machines must be able to follow these same cues.
Three Levels of Hypermedia-Driven Interaction
To move from "REST-ish" to truly RESTful, we must stop forcing clients to memorize our URL structures and start sending them instructions.
1. The Human Standard: HTML
We already know how to do this for humans. When a browser receives an HTML document, it doesn't need an API manual to know how to "Add to Cart." The <form> tag tells the browser the URL, the method, and the required fields.
<form action="/cart/items" method="POST">
<input type="hidden" name="product_id" value="prod_987">
<button type="submit">Add to Cart</button>
</form>
The browser discovers the interaction, and the user executes it.
2. The Machine Standard: HAL+JSON
For machine-to-machine communication, we need a similar language. HAL (Hypertext Application Language) is a popular choice for expressing relationships. Instead of a static data block, the API returns a set of available links dynamically based on the resource's current state.
{
"order_id": "ord_1024",
"status": "awaiting_payment",
"_links": {
"self": { "href": "/orders/ord_1024" },
"payment": { "href": "/orders/ord_1024/pay", "title": "Submit payment" }
}
}
If the order is already paid, the server simply omits the payment link. The client doesn't need if/else logic to guess if payment is allowed—it simply checks if the link exists.
3. The Intelligent Automaton: JSON-LD + Hydra
For highly decoupled systems, we can use JSON-LD paired with Hydra. This takes it a step further by providing a semantic vocabulary that tells the client exactly what is expected.
{
"@context": [
"http://www.w3.org/ns/hydra/context.jsonld",
{ "@vocab": "http://schema.org/" }
],
"@id": "/users/johndoe",
"@type": "Person",
"name": "John Doe",
"email": "john@example.com",
"operation": [
{
"@type": "ReplaceResourceOperation",
"method": "PUT",
"expects": "http://schema.org/Person",
"title": "Update profile details"
}
],
"operation": [ {
"@type": "ReplaceResourceOperation",
"method": "PUT",
"expects": "http://schema.org/Person",
"title": "Update profile details"
}
]
}Here, the client understands the context, the operation type, the HTTP method, and the expected data structure. No out-of-band documentation (like a PDF or an OpenAPI spec) is required to perform the update.
Why This Matters: The Shift to State Machines
When you build APIs this way, you change the nature of your client. Instead of a rigid script that breaks the moment you change a URL, your client becomes a dynamic state machine:
The Affordance is King: If the user has permission to edit, the server sends an
editlink. If they don't, the link is absent.No Hardcoded URLs: The client only needs the root URL. Every subsequent action is discovered through the payload.
Protocol-Driven: By using standard media types like
application/hal+json, your API becomes self-documenting.
True REST isn't just about using HTTP verbs—it’s about letting the server drive the application state through hypermedia. It requires a shift in mindset, but the result is a system that is significantly more flexible, discoverable, and resilient to change.
How about REST and OpenAPI, can both technologies be combined?
To understand the relationship between REST (with HATEOAS) and OpenAPI, it helps to view them not as competing technologies, but as serving different functions in the lifecycle of an API.
The Core Difference: Implementation vs. Documentation
REST (with HATEOAS) is an architectural style. It defines how your API behaves and how a client interacts with it.
HATEOAS (Hypermedia as the Engine of Application State) is the "glory" of REST, where the server provides links within the response to guide the client to the next possible states. OpenAPI is a specification (a blueprint). It is a standard for describing an API.
It is not the API itself; rather, it is a document that tells developers and machines what endpoints exist, what parameters they accept, and what they return.
| Feature | REST (with HATEOAS) | OpenAPI |
| Purpose | Defines architectural constraints/behavior. | Documents and describes the API. |
| Focus | How the client discovers state transitions via links. | What the endpoints, schemas, and methods are. |
| Primary Goal | Decoupling the client from the server's URL structure. | Enabling automation, tooling, and developer onboarding. |
| State | The server dictates the current state via hypermedia links. | The document is static and describes potential states. |
The Tension: Why They "Don't Work Together Nicely"
You may encounter friction when trying to combine them because their underlying philosophies can contradict each other:
REST/HATEOAS says: "The client shouldn't need to know the URL structure beforehand; it should just follow the links provided by the server".
OpenAPI says: "Here is a complete map of all URLs and structures so you can build your client right now".
When you use HATEOAS, you are intentionally trying to avoid "hardcoding" URL paths in the client. However, OpenAPI is designed specifically to document those exact paths. When you use an OpenAPI generator to create client code, it often generates static URL-building logic, which effectively defeats the purpose of the dynamic discovery that HATEOAS aims to provide.
Can They Coexist?
Yes, they are often used together, but it requires a careful approach:
Documentation vs. Navigation: You can use OpenAPI to describe the "capabilities" of your API (the resources, the schemas, and the entry points) while using HATEOAS within the actual API responses to handle the specific navigational flow and dynamic state transitions.
Extended Definitions: Some developers include custom link definitions within their OpenAPI files to represent the hypermedia relationships, though standard OpenAPI does not natively "force" the HATEOAS behavior.
Framework Support: Libraries like
springdoc-openapi-hateoasexist specifically to bridge this gap, attempting to help OpenAPI documentation recognize and represent the HATEOAS links that your code generates.
Summary: Think of the REST API as the building itself (its layout, its doors, and its hallways) and OpenAPI as the architectural blueprint or map you give to visitors. You can have a building that is designed for people to navigate by signs (HATEOAS), and you can still provide a map (OpenAPI) so they know generally where they are going—just be careful not to make your map so rigid that it breaks the navigation design of the building.
No comments:
Post a Comment