When to Use Affordance vs. Link in Hypermedia Controls
Hypermedia as the engine of application state (HATEOAS) brings a new level of discoverability and self-documentation to REST APIs. In Spring HATEOAS, two key concepts play a central role in enriching your API responses with navigable metadata: Link and Affordance. Understanding when to use each—and how they differ—is crucial for building intuitive, flexible APIs that clients can understand and act on without relying on out-of-band documentation.
Understanding the Basics
What is a Link?
A Link in Spring HATEOAS represents a navigable hyperlink that a client can follow. It might point to another resource or to a collection of resources. Each Link typically includes a rel (relation) type that describes its purpose in context (e.g., self, next, update, etc.).
Example:
Link link = Link.of("/orders/123").withRel("self");
What is an Affordance?
An Affordance builds on a Link by describing what actions are possible at the linked URI. This goes beyond just navigating to a resource—it tells the client what HTTP operations are available (e.g., POST, PUT, DELETE) and includes metadata such as input fields, constraints, and expected formats.
Example:
Link linkWithAffordance = Link.of("/orders/123")
.andAffordance(Affordances.of(OrderController.class).updateOrder(null, 123L));
This link not only tells the client how to get the order, but also how to update it.
Key Differences Between Link and Affordance
| Feature | Link | Affordance |
|---|---|---|
| Purpose | Navigation | Action and interaction |
| HTTP Verb | Typically GET | Any (GET, POST, PUT, DELETE, PATCH, etc.) |
| Input Metadata | None | Includes form structure, input fields, constraints |
| Self-descriptive API | Partial | Full (with method semantics and expectations) |
| Support in HAL-FORMS | Limited (only navigation) | Full (used for rendering forms and available actions) |
When to Use Each
✅ Use Link When:
- You only want to provide navigation to another resource.
- You are linking to read-only endpoints (e.g.,
GET /users/123). - You don’t need to describe possible state changes or operations.
Example Use Case:
Linking from an order list to an individual order detail page.
orderModel.add(Link.of("/orders/123").withSelfRel());
✅ Use Affordance When:
- You want the client to understand available actions (e.g., “submit”, “update”, “cancel”).
- You’re building a hypermedia form or supporting HAL-FORMS.
- You want to generate dynamic UI from API metadata.
Example Use Case:
Enabling clients to submit an update to an order without separate documentation.
orderModel.add(Link.of("/orders/submit").withRel("submit"));
In a HAL-FORMS response, this affordance would be rendered as a form with proper input fields.
Best Practices
- Start with Links: Use simple
Linkobjects for read-only navigation. - Add Affordances Where Needed: Introduce
Affordancewhen you want to support client interaction (especially PUT, POST, DELETE). - Be Intentional with
rel: Whether adding a link or affordance, clearly define the relationship type (e.g.,update,delete,submit-form). - Avoid Overloading Clients: Don’t add affordances to every link—use them where actions are expected or useful.
- Test with HAL-FORMS Clients: If you’re exposing affordances, test with tools that understand and render HAL-FORMS (like Spring HATEOAS HAL browser).
Common Pitfall: Using Link When You Need More
Developers often create a link expecting the client to know how to POST to it or what fields are required. This violates the HATEOAS principle. Use affordances to make this self-descriptive:
❌ Not ideal:
orderModel.add(
linkTo(methodOn(OrderController.class).getOrder(123L))
.withSelfRel()
.andAffordance(afford(methodOn(OrderController.class).updateOrder(null, 123L)))
);
✅ Better with affordance:
orderModel.add(
Link.of("/orders/submit")
.withRel("submit")
.andAffordance(afford(methodOn(OrderController.class).submitOrder(null)))
);
Summary
| If You Need To… | Use Link | Use Affordance |
|---|---|---|
| Provide navigation | ✅ | ✅ (optional) |
| Describe allowed operations (e.g., PUT) | ❌ | ✅ |
| Enable form generation via HAL-FORMS | ❌ | ✅ |
| Keep response lightweight | ✅ | ❌ |
In short, use Link for direction and Affordance for interaction. By using them wisely, you’ll create APIs that are not just consumable—but discoverable and self-describing.




