# Hypermedia API with Spring HATEOAS
Did you ever consider the quality of your REST API? Did you know there are several levels of REST API? Did you ever heard the term HATEOAS? Or maybe you ever wonder how to implement it in Java? If so then in this articule we'll cover anwsers to that questions with main emphasis on HATEOAS concept and implementation of that concept with Spring HATEOAS project.
## What is HATEOAS?
**H**ypermedia **A**s **T**he **E**ngine **O**f **A**pplication **S**tate - is one of the constraints of the REST architecture. Neither REST nor HATEOAS is any requirement or specification. How you implement it depends only on you. At this point, you may ask yourself - how RESTful your api is without using HATEOAS?
This question is answered by the REST maturity model presented by Leonard Richardson. This model consists of four levels, as set out below:
* level 0 - the API implementation uses the HTTP protocol, but not using it's full capabilites, additionally, unique addresses for resources are not provided.
* level 1 - we have a unique identifier for the resource, but each action on the resource has its own url
* level 2 - we use HTTP methods instead of verbs describing actions, e.g. DELETE method instead of url `.../delete`
* level 3 - the term HATEOAS has been introduced. Simply speaking it introduces hypermedia to resources. This allows you to place links in the response informing about possible actions, thereby adding possibility to navigate through API.

Most projects are currently written using level 2. If we would like to go for the perfect RESTful API, we should consider HATEOAS.

Above is an example of a response from the server in the form of JSON+HAL. Such a resource consists of two parts: our data and links to actions that are possible to be performed on a given resource.
## SPRING HATEOAS 1.x.x
You may be asking yourself how to implement HATEOAS in Java? You can of course write your solution, but why reinvent the wheel? The right tool for this seems to be the Spring Hateoas project. It is a long-standing solution on the market because its origins date back to 2012, but in 2019 we had a version 1.0 release. Of course, this version introduced a few changes compared to 0.x. They will be discussed at the end of the article after presenting some examples of using this library so that you better understand what the differences between the two versions are.
Let's discuss the possibilities of the library based on a simple API that returns us a list of movies and related directors. Our domain looks like this:
```java
@Entity
public class Movie {
@Id
@GeneratedValue
private Long id;
private String title;
private int year;
private Rating rating;
@ManyToOne
private Director director;
}
@Entity
public class Director {
@Id
@GeneratedValue
@Getter
private Long id;
@Getter
private String firstname;
@Getter
private String lastname;
@Getter
private int year;
@OneToMany(mappedBy = "director")
private Set<Movie> movies;
}
```
We can approach to the implementation of HATEOAS in several ways. The three methods represented here are ranked from least to most recommended.
But first we need to add some dependencies to our Spring Boot project:
```xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-hateoas</artifactId>
</dependency>
```
Ok, now we can consider implementation options.
### 1. Entity extends RepresentationModel with links directly in Controller class
Firstly extend our entity models with RepresentationModel.
```java
public class Movie extends RepresentationModel<Movie>
public class Director extends RepresentationModel<Director>
```
Then add links to RepresentationModel within each controller. The example below returns all directors from the system. By adding two links to each director - to himself and to the entire collection. A link is also added to the collection. The key elements of this code are two methods with static imports:
* `linkTo()` - responsible for creating the link
* `methodOn()` - enables to dynamically generate the path to a given resource. We don't need to hardcode the path but we can refer to the method in the controller
```java
@GetMapping("/directors")
public ResponseEntity<CollectionModel<Director>> getAllDirectors() {
List<Director> directors = directorService.getAllDirectors();
directors.forEach(director -> {
director.add(linkTo(methodOn(DirectorController.class).getDirectorById(director.getId())).withSelfRel());
director.add(linkTo(methodOn(DirectorController.class).getDirectorMovies(director.getId())).withRel("directorMovies"));
});
Link allDirectorsLink = linkTo(methodOn(DirectorController.class).getAllDirectors()).withSelfRel());
return ResponseEntity.ok(CollectionModel.of(directors, allDirectorsLink));
}
```
This is the response we get after invoking such controller:

We can get similar result when requesting for a specific resource.
```java
@GetMapping("/directors/{id}")
public ResponseEntity<Director> getDirector(@PathVariable("id") Long id) {
return directorService.getDirectorById(id)
.map(director -> {
director.add(linkTo(methodOn(DirectorController.class).getDirectorById(id)).withSelfRel());
director.add(linkTo(methodOn(DirectorController.class).getDirectorMovies(id)).withRel("directorMovies"));
director.add(linkTo(methodOn((DirectorController.class)).getAllDirectors()).withRel("directors"));
return ResponseEntity.ok(director);
})
.orElse(ResponseEntity.notFound().build());
}
```

The main advantage of this implementation is simplicity. But making our entity dependent on an external library is not a very good idea. Plus, the code repetition for adding links for a specific resource is immediately noticeable. You can of course bring it to some private method, but there is a better way.
### 2. Use Assemblers - SimpleRepresentationModelAssembler
And it's not about assembly language, but about a special kind of class that convert our resource to RepresentationModel.
One of such assemblers is SimpleRepresentationModelAssembler, its implementation is as follows:
```java
@Component
public class DirectorAssembler implements SimpleRepresentationModelAssembler<Director> {
@Override
public void addLinks(EntityModel<Director> resource) {
Long directorId = resource.getContent().getId();
resource.add(linkTo(methodOn(DirectorController.class).getDirectorById(directorId)).withSelfRel());
resource.add(linkTo(methodOn(DirectorController.class).getDirectorMovies(directorId)).withRel("directorMovies"));
}
@Override
public void addLinks(CollectionModel<EntityModel<Director>> resources) {
resources.add(linkTo(methodOn(DirectorController.class).getAllDirectors()).withSelfRel());
}
}
```
In this case, our entity will be wrapped in an `EnityModel` (this class extends `RepresentationModel`) to which the links specified by us in the `addLinks()` will be added. Here we overwrite two `addLinks()` methods - one for entire data collections and the other for single resources.
Then, as part of the controller, it is enough to call the `toModel()` or `toCollectionModel()` method (`addLinks()` are template methods here), depending on whether we return a collection or a single representation.
```java
@GetMapping
public ResponseEntity<CollectionModel<EntityModel<Director>>> getAllDirectors() {
return ResponseEntity.ok(directorAssembler.toCollectionModel(directorService.getAllDirectors()));
}
@GetMapping(value = "directors/{id}")
public ResponseEntity<EntityModel<Director>> getDirectorById(@PathVariable("id") Long id) {
return directorService.getDirectorById(id)
.map(director -> {
EntityModel<Director> directorRepresentation = directorAssembler.toModel(director)
.add(linkTo(methodOn(DirectorController.class).getAllDirectors()).withRel("directors"));
return ResponseEntity.ok(directorRepresentation);
})
.orElse(ResponseEntity.notFound().build());
}
```
The main benefit of using the `SimpleRepresentationModelAssembler` is the separation of our entity from the `RepresentationModel`, as well as the separation of the adding link logic from the controller.
The problem arises when we want to add hypermedia to nested elements of an object. Obtaining the effect as in the example below is impossible with current way.
```json
{
"id": "M0002",
"title": "Once Upon a Time in America",
"year": 1984,
"rating": "R",
"directors": [
{
"id": "D0001",
"firstname": "Sergio",
"lastname": "Leone",
"year": 1929,
"_links": {
"self": {
"href": "http://localhost:8080/directors/D0001"
}
}
}
],
"_links": {
"self": {
"href": "http://localhost:8080/movies/M0002"
}
}
}
```
### 3. Create DTO class with RepresentationModelAssembler
The solution to this problem is to combine the two previous methods, modifying them slightly.
In our opinion, `RepresentationModelAssembler` offers the most possibilities. It removes the restrictions that arose in the case of nested elements for `SimpleRepresentationModelAssembler`. But it also requires more code from us because we need to prepare a DTOs, which are often done anyway.
This is the implementation based on `RepresentationModelAssembler`:
```java
@Component
public class DirectorRepresentationAssembler implements RepresentationModelAssembler<Director, DirectorRepresentation> {
@Override
public DirectorRepresentation toModel(Director entity) {
DirectorRepresentation directorRepresentation = DirectorRepresentation.builder()
.id(entity.getId())
.firstname(entity.getFirstname())
.lastname(entity.getLastname())
.year(entity.getYear())
.build();
directorRepresentation.add(linkTo(methodOn(DirectorController.class).getDirectorById(directorRepresentation.getId())).withSelfRel());
directorRepresentation.add(linkTo(methodOn(DirectorController.class).getDirectorMovies(directorRepresentation.getId())).withRel("directorMovies"));
return directorRepresentation;
}
@Override
public CollectionModel<DirectorRepresentation> toCollectionModel(Iterable<? extends Director> entities) {
CollectionModel<DirectorRepresentation> directorRepresentations = RepresentationModelAssembler.super.toCollectionModel(entities);
directorRepresentations.add(linkTo(methodOn(DirectorController.class).getAllDirectors()).withSelfRel());
return directorRepresentations;
}
}
```
When it comes to controller methods, they look the same as for `SimpleRepresentationModelAssembler`, the only difference is that in the `ResponseEntity` the return type is DTO - `DirectorRepresentation`.
```java
@GetMapping
public ResponseEntity<CollectionModel<DirectorRepresentation>> getAllDirectors() {
return ResponseEntity.ok(directorRepresentationAssembler.toCollectionModel(directorService.getAllDirectors()));
}
@GetMapping(value = "/{id}")
public ResponseEntity<DirectorRepresentation> getDirectorById(@PathVariable("id") String id) {
return directorService.getDirectorById(id)
.map(director -> {
DirectorRepresentation directorRepresentation = directorRepresentationAssembler.toModel(director)
.add(linkTo(methodOn(DirectorController.class).getAllDirectors()).withRel("directors"));
return ResponseEntity.ok(directorRepresentation);
})
.orElse(ResponseEntity.notFound().build());
}
```
Here is our DTO model:
```java
@Builder
@Getter
@EqualsAndHashCode(callSuper = false)
@Relation(itemRelation = "director", collectionRelation = "directors")
public class DirectorRepresentation extends RepresentationModel<DirectorRepresentation> {
private final String id;
private final String firstname;
private final String lastname;
private final int year;
}
```
The `@Relation` annotation allows you to configure the relationship names to be used in the HAL representation.
Without it, the relationship names match the class name and a suffix List for the collection.
By default JSON+HAL looks like this
```json
{
"_embedded": {
"directorRepresentationList": [
…
]
},
"_links": {
…
}
}
```
Hovever annotation @Relation can change name for `directors`
```json
{
"_embedded": {
"directors": [
…
]
},
"_links": {
…
}
}
```
Summarizing the HATEOAS concept it consists of few pros and cons.
#### Pros:
* If the client uses it, we can change the API address for our resources without breaking the client
* Creates good self documentation, and table of contents of API to person who have first contatct with our API
* Can simplify building some conditions on frontend, e.g. whether the button should be disabled / enabled based on whether the link to corresponding the action exists
* Less coupling between frontend and backend
* Just like writing tests imposes on us to stick to the SRP principle in class construction, hateoas can keep us in check when designing API
#### Cons:
* Additional work needed on implementing non-business functionality
* Additional network overhead. The size of the transferred data is larger
* Adding links to some resources can be sometimes complicated and can introtuce mess in controllers
## Changes in Spring HATEOAS 1.0
Spring HATEOAS has been available since 2012, but first release of version 1.0 was in 2019.
The main changes concerned the changes to the package paths and names of some classes, e.g.
| Old | New |
| -------- | -------- |
| ResourceSupport | RepresentationModel |
| Resource | EntityModel |
| Resources | CollectionModel |
| PagedResources | PagedModel |
| ResourceAssembler | RepresentationModelAssembler |
It is worth paying attention to a certain naming convention -
the replacement of the word `Resource` in class names with the word `Representation`, this is because these types do not represent resources but representations, which can be enriched with hypermedia. It is also more in the spirit of REST. We are returning the resource representations, not the resources themselves. In the new version, there is a tendency to move away from constructors in favor of static construction methods - `.of()`.
It is also worth mentioning that the old version has no equivalent for `SimpleRepresentationModelAssembler`. On the other hand, the `ResourceAssembler` interface has only the `toResource()`method (equivalent - `toModel()`) and no equivalent for `toCollectionModel()`. Such a method is found in `RepresentationModelAssembler` and is the` toModelCollection()` method.
The creators of the library have also included a script that migrates old package paths and old class names to the new version. You can check it here https://docs.spring.io/spring-hateoas/docs/1.0.0.M1/reference/html/#migrate-to-1.0
## Sources
https://docs.spring.io/spring-hateoas/docs/1.0.0.M1/reference/html/
https://spring.io/guides/gs/rest-hateoas/
https://spring.io/guides/tutorials/rest/
https://github.com/spring-projects/spring-hateoas-examples
https://martinfowler.com/articles/richardsonMaturityModel.html
https://dzone.com/articles/applying-hateoas-to-a-rest-api-with-spring-boot
https://spring.io/blog/2018/01/12/building-richer-hypermedia-with-spring-hateoas
https://geek.justjoin.it/hateoas-api-moze-byc-lepsze
https://www.baeldung.com/spring-hateoas-tutorial