Most developers are familiar with building REST APIs using Spring Boot, but fewer take advantage of HATEOAS — Hypermedia as the Engine of Application State. HATEOAS enables your API to be self-descriptive, allowing clients to navigate available operations dynamically via links provided in the responses.

In this article, we’ll explore how to use Spring HATEOAS to enhance your REST APIs, making them more maintainable, intuitive, and compliant with REST constraints.


What is HATEOAS?

HATEOAS is a key constraint of REST that requires responses to contain hypermedia links to related resources and actions.

Instead of just returning data, your API tells the client what it can do next.

Example (non-HATEOAS):

{
"id": 1,
"name": "Alice"
}

With HATEOAS:

{
"id": 1,
"name": "Alice",
"_links": {
"self": { "href": "/users/1" },
"orders": { "href": "/users/1/orders" }
}
}

This approach increases API usability and makes client interactions more dynamic and resilient.


Adding Spring HATEOAS to Your Project

Add the following dependency:

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-hateoas</artifactId>
</dependency>

Spring HATEOAS automatically adds support for hypermedia types like HAL (Hypertext Application Language).


Spring HATEOAS provides EntityModel to wrap your response object and add links.

@GetMapping("/users/{id}")
public EntityModel<UserDTO> getUser(@PathVariable Long id) {
UserDTO user = userService.getUserById(id);

    return EntityModel.of(user,
        linkTo(methodOn(UserController.class).getUser(id)).withSelfRel(),
        linkTo(methodOn(OrderController.class).getOrdersByUser(id)).withRel("orders"));
}

This wraps the UserDTO with self and related links, creating a richer, navigable response.


Use CollectionModel for lists of resources:

@GetMapping("/users")
public CollectionModel<EntityModel<UserDTO>> allUsers() {
List<EntityModel<UserDTO>> users = userService.getAllUsers().stream()
.map(user -> EntityModel.of(user,
linkTo(methodOn(UserController.class).getUser(user.getId())).withSelfRel()))
.collect(Collectors.toList());

    return CollectionModel.of(users,
        linkTo(methodOn(UserController.class).allUsers()).withSelfRel());
}

This helps clients discover all users and available operations for each.


Supporting HAL and Hypermedia Types

By default, Spring HATEOAS supports HAL. You can specify the desired format via headers:

Accept: application/hal+json

To ensure correct behavior, expose the correct content type using Spring Boot’s auto-configured content negotiation.


Using RepresentationModelAssembler

To avoid duplicating link logic, implement RepresentationModelAssembler:

@Component
public class UserModelAssembler implements RepresentationModelAssembler<UserDTO, EntityModel<UserDTO>> {

    @Override
    public EntityModel<UserDTO> toModel(UserDTO user) {
        return EntityModel.of(user,
            linkTo(methodOn(UserController.class).getUser(user.getId())).withSelfRel(),
            linkTo(methodOn(UserController.class).allUsers()).withRel("users"));
    }
}

This centralizes the logic of link creation, promoting reuse and consistency.


Advantages of Using HATEOAS

  • Discoverability: Clients can explore the API dynamically
  • Loose coupling: Fewer hardcoded paths on the client side
  • Evolvability: APIs can change without breaking clients
  • Improved UX: APIs guide clients on available actions

This leads to better integration across services, especially in distributed systems and microservices.


Best Practices

  • Include self links in every resource
  • Add relationship links to related resources
  • Avoid overloading responses with unnecessary links
  • Use RepresentationModelAssembler to maintain clean code
  • Document link structure and relationships clearly

Conclusion

Designing RESTful APIs with Spring Boot and HATEOAS creates a self-navigable interface that simplifies client development and makes your API more expressive and resilient to change.

By leveraging EntityModel, CollectionModel, and hypermedia types like HAL, your Spring Boot application can evolve into a truly REST-compliant, user-friendly, and maintainable API.