Add HATEOAS to Spring Boot RESTful Web Service

HATEOAS is a way to make your RESTful Web Service endpoint, automatically include links to other Resources of your API, in the response that it sends back to a calling client application. 

The client application that consumes your web service endpoint, can then use those links, to consume other RESTful Resources that your Web Service provides.

Let’s say we have a client application that sends HTTP Request to a RESTful web service endpoint to get user details. The web service will respond back with a JSON or XML representation of user details. An example of such a response can be the following JSON representation of user details: 

{
    "userId": "CfvqLv5fyDCu3sSZiHFJRnsWkZ4ZOk",
    "firstName": "Sergey",
    "lastName": "Kargopolov",
    "email": "[email protected]"
}

Having received the above response, the client application has no knowledge about other web service endpoints that it can consume to get additional information about this user. For example, it does not know that this user has two different addresses and that there is a separate web service endpoint to get a list of all user addresses and a separate web service endpoint to get the details of a single address.

With the help of HATEOAS, we can make our Web Service endpoint include links to those other useful web service endpoints the client application can call and get additional details about the user.

Add HATEOAS Dependency to POM.XML

To add HATEOAS support to your Spring Boot application add the following dependency to a pom.xml file of your Spring Boot application.

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

Web Service Endpoint

Now when we have added HATEOAS dependency to our Spring Boot application we can start adding links to a model object that our RESTful Web Service returns. Let’s assume we have the following RESTful Web Service endpoint that returns an object of UserRest class.

@GetMapping(path = "/{id}")
public UserRest getUser(@PathVariable String id) {
    
    UserRest returnValue = userService.getUserByUserId(id);

    return returnValue;
}

Extending RepresentationModel

The above RESTful Web Service endpoint returns an object of UserRest class. One of the ways to add links to an object of UserRest class is to make it extend the RepresentationModel that comes from org.springframework.hateoas package. If you cannot make UserRest class extend the RepresentationModel, then there is another way to add links to which we will discuss a bit later in this blog post.

import org.springframework.hateoas.RepresentationModel;

public class UserRest extends RepresentationModel{
    private String userId;
    private String firstName;
    private String lastName;
    private String email;   

   // Setters and Getters need go below. 
   ...
}

Adding Links to Response Model Class

Now when our UserRest class extends the RepresentationModel class, we can start adding links to it.

@GetMapping(path = "/{id}")
public EntityModel<UserRest> getUser(@PathVariable String id) {
 
    // Get User Details
    UserRest returnValue = userService.getUserByUserId(id);
       
    // Create links
    Link userResourceLink = linkTo(UserController.class).slash(id).withSelfRel();
    Link addressesResourceLink = linkTo(UserController.class).slash(id).slash("addresses").withRel("addresses");

    // Add links
    returnValue.add(userResourceLink);
    returnValue.add(addressesResourceLink);

    return EntityModel.of(returnValue);

}

This will make the above Web Service endpoint return the following JSON Representation of UserRest object.

{
    "userId": "enIY0wW4leHgWneVXTopxJH3MAbOV2",
    "firstName": "Sergey",
    "lastName": "Kargopolov",
    "email": "[email protected]",
    "_links": {
        "self": {
            "href": "http://localhost:8080/mobile-app-ws/users/enIY0wW4leHgWneVXTopxJH3MAbOV2"
        },
        "addresses": {
            "href": "http://localhost:8080/mobile-app-ws/users/enIY0wW4leHgWneVXTopxJH3MAbOV2/addresses"
        }
    } 
}

Using EntityModel to Add Links

Another way of adding links to UserRest class is to use the EntityModel class. In this case, there is no need to make your UserRest class extend the RepresentationModel class.

To add links to an object of UserRest class, you can wrap it into an EntityModel and call the add() method and give it either a single Link or a collection of Links.

EntityModel.of(returnValue).add(userResourceLink).add(addressesResourceLink);

The below code snippet uses the EntityModel to add links. In this case, UserRest class does not extend the RepresentationModel.

@GetMapping(path = "/{id}")
public EntityModel<UserRest> getUser(@PathVariable String id) {
  
    UserRest returnValue = userService.getUserByUserId(id);
       
    Link userResourceLink = linkTo(UserController.class).slash(id).withSelfRel();
    Link addressesResourceLink = linkTo(UserController.class).slash(id).slash("addresses").withRel("addresses");
 
    return EntityModel.of(returnValue, userResourceLink, addressesResourceLink);
}

Adding Links to a Collection of Resources

If our web service endpoint needs to return a collection of objects, like for example, a list of all addresses, then we need to wrap the resources we are returning into a CollectionModel type.

CollectionModel.of(listOfAddresses);

This will make our web service still return a list of addresses, but because we need to add links to this collection of addresses it will be wrapped into another JSON object.

@GetMapping(path = "/{id}/addresses")
public CollectionModel<AddressesRest> getUserAddresses(@PathVariable String id) {
    List<AddressesRest> listOfAddresses = new ArrayList<>();

    // code to populate the listOfAddresses object with addresses

    return CollectionModel.of(listOfAddresses);  
}

To add a link to a CollectionModel, you will need to first create a link and then add it to a CollectionModel the following way:

// Create a new link
Link userLink = linkTo(methodOn(UserController.class).getUser(id)).withRel("user");

// Add link to a CollectionModel and return result
return CollectionModel.of(addressesListRestModel, userLink);

The CollectionModel.of() can accept a single link, a Collection of links, or a variable parameter of Links. So if you need to add more links, you can easily do it.

You can also use the add() method and add the Link object in the following way:

CollectionModel.of(addressesListRestModel).add(userLink);

I hope this tutorial was of some value to you. To learn more about how to build RESTful Web Services with Spring Boot, have a look at my other tutorials in Spring Boot and Spring MVC category on this site.

Leave a Reply

Your email address will not be published. Required fields are marked *