Creating A RESTful API With HATEOAS Using Spring HATEOAS
A well-designed REST API should give clients more than JSON data. It should also explain what a client can do next. HATEOAS, or Hypermedia as the Engine of Application State, adds links and actions to API responses so that consumers can navigate resources without hard-coding every endpoint.
Spring HATEOAS provides the Java building blocks for this approach. It integrates neatly with Spring Boot, Spring MVC, validation, Spring Data, and common serialisation tools, making it practical for production systems rather than just a theoretical REST constraint.
Imagine an order API used by a Sydney retailer, a Melbourne payments platform, or a Brisbane delivery service. A client might need to view an order, pay for it, cancel it, or follow its shipment. Hypermedia links can advertise the actions currently available for that order.
The examples below use a simple product resource, but the same design works for customer accounts, invoices, bookings, and support tickets. Australian systems may also need fields such as an AUD price, an Australian postcode, or timestamps displayed consistently across AEST and other local time zones.
Why Hypermedia Matters
A conventional response may return product fields and leave the frontend to guess that /api/products/42/reviews contains reviews. With HATEOAS, the response includes a link with a relation such as reviews. The client follows that relation instead of relying on a fixed URL structure.
This reduces coupling between the API and consumers. If a route changes from /api/products/42/reviews to another location, the client can continue working as long as the link relation remains meaningful. It is particularly useful when mobile apps, partner integrations, and web clients evolve at different speeds.
Set Up A Spring HATEOAS Project
For a Maven-based Spring Boot application, add the HATEOAS starter alongside Web and validation support:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-hateoas</artifactId>
</dependency>
A resource representation can extend RepresentationModel, which provides a place for links:
public class ProductModel extends RepresentationModel<ProductModel> {
private Long id;
private String name;
private BigDecimal price;
// constructors, getters and setters
}
Use BigDecimal for prices rather than floating-point values. This matters when displaying AUD amounts, calculating GST, or reconciling payment transactions to the cent.
Model Resources And Links
A controller can convert a database entity into a hypermedia representation. WebMvcLinkBuilder creates links based on controller methods, helping prevent URLs from becoming scattered string literals:
@GetMapping("/{id}")
public EntityModel<Product> findById(@PathVariable Long id) {
Product product = service.findById(id);
return EntityModel.of(product,
linkTo(methodOn(ProductController.class)
.findById(id)).withSelfRel(),
linkTo(methodOn(ProductController.class)
.findAll()).withRel("collection"));
}
The self relation identifies the current resource, while collection points to the broader product list. You can add business actions conditionally. For example, an unpaid order might expose pay and cancel, while a completed order exposes receipt but no longer offers cancellation.
Build Collection And Item Endpoints
Collections and individual resources need different representations. A collection response can use CollectionModel and add a link to itself:
@GetMapping
public CollectionModel<EntityModel<Product>> findAll() {
List<EntityModel<Product>> products = service.findAll()
.stream()
.map(product -> EntityModel.of(product,
linkTo(methodOn(ProductController.class)
.findById(product.getId())).withSelfRel()))
.toList();
return CollectionModel.of(products,
linkTo(methodOn(ProductController.class).findAll())
.withSelfRel());
}
For pagination, include first, prev, next, and last links when those pages exist. A client in Perth should not need to understand your database paging strategy, and a mobile application should be able to follow next without reconstructing query parameters itself.
Frontend teams can consume these responses using ordinary HTTP clients. When choosing how a public documentation site or API portal renders dynamic content, these frontend rendering options can also affect how easily live API data is presented.
Handle Actions And Errors Clearly
HATEOAS links can represent state transitions, not just navigation. An order response might contain:
{
"id": 18,
"status": "READY_FOR_PAYMENT",
"_links": {
"self": { "href": "/api/orders/18" },
"pay": { "href": "/api/orders/18/payment" },
"cancel": { "href": "/api/orders/18" }
}
}
The absence of a link is meaningful. A client should treat it as an unavailable action rather than displaying a button that will predictably fail. Use consistent HTTP status codes and structured error responses for validation failures, missing resources, and unauthorised operations.
For Australian applications, error messages may need to distinguish an invalid four-digit postcode from a missing suburb, while payment workflows should avoid exposing sensitive card details. Spring Security can protect action endpoints, and audit logs should record who changed an order and when.
Practical Delivery Checklist
Start with a small resource and make its links useful before applying hypermedia across the entire platform. Test responses with MockMvc or Spring Boot integration tests, checking both the data fields and the link relations.
A production API should also document media types, authentication requirements, pagination behaviour, and versioning rules. A Sydney-based team working with a client in Adelaide should agree on UTC storage and clear timezone conversion rather than relying on server defaults.
- Use
selflinks consistently for individual resources. - Give actions stable, meaningful relation names such as
pay,cancel, orreviews. - Build URLs with controller methods instead of manually concatenating paths.
- Include pagination links for large collections.
- Test that unavailable actions are omitted in each resource state.
- Protect state-changing operations with authentication and authorisation.
Spring HATEOAS works best when link design follows the business domain. Treat each response as a navigable resource, expose only valid next actions, and let clients follow the API’s controls instead of depending on undocumented URL assumptions.