Search results
Adding HAL pagination links to RESTful applications using Spring HATEOAS
1. OVERVIEW
Often times API endpoint implementations involve retrieving data from some sort of storage. Retrieving data, even when filtering based on a search criteria might result in hundreds, thousands or millions of records. Retrieving such amount of data could lead to performance issues, not meeting a contracted SLA, ultimately affecting the user experience.
One approach to overcome this problem is to implement pagination. You could retrieve a number of records from a data storage and add pagination links in the API response along with the page metadata back to the client application.
In a previous post, I showed readers how to include HAL hypermedia in Spring Boot RESTful applications using HATEOAS. Adding related links to REST responses help the client applications deciding what they might do next.
Some of the next actions a client application could help a customer do is to navigate through a list of resources. For instance to the first page of a result list.
This is a follow-up blog post to help you adding HAL (Hypertext Application Language) pagination hypermedia to your API responses using Spring Boot 2.1.x.RELEASE and Spring HATEOAS 0.25.x.RELEASE.
2. REQUIREMENTS
- Java 8+.
- Maven 3.2+.
- Familiarity with Spring Framework.
3. HATEOAS PAGINATION IMPLEMENTATION
Adding HAL hypermedia to API responses using Spring HATEOAS, paginated or not, require the spring-boot-starter-hateoas
dependency.
pom.xml
:
The RESTful responses including HAL links will be sent in application/hal+json
format by default. To override this behavior you can set the use-hal-as-default-json-media-type
property to false.
application.yml
:
3.1. @EnableSpringDataWebSupport
Application.java
:
The @EnableSpringDataWebSupport annotation registers a number of beans Spring MVC controllers use. They support pagination, sorting, and resources assembler injection. The latter if Spring HATEOAS is found in the classpath.
3.2. REST CONTROLLER
Let’s analyze FilmController.retrieveFilms() endpoint implementation.
Notice that Pageable pageRequest and PagedResourcesAssembler<Film> pagedResourcesAssembler arguments are injected into the method. This behavior is the result of adding @EnableSpringDataWebSupport to your application.
Method argument | Description |
---|---|
pageRequest | Allows to send requests to this endpoint as /api/films?page=0&size=30 . |
pagedResourcesAssembler | Helps adding first , previous , self , next , last navigation links along with page metadata to the response payaload as included in the requests/responses examples section. |
This endpoint implementation first retrieves Film JPA entities then assembles the PagedResources response. The most relevant statement related to pagination is:
How are the pagedResourcesAssembler.toResource() arguments used?
pagedResourcesAssembler.toResource() uses these arguments:
-
selfLink method argument: Used to add the page and size request parameters while keeping the rest of the query parameters.
For instance, for an incoming request like/api/films?param1=value¶m2=value2&page=3&size=30
, the pagination previous link would look like/api/films?param1=value¶m2=value2&page=3&size=30
.
If you don’t pass the selfLink,param1=value
andparam2=value2
request parameters won’t be included. -
films method argument: A Page of the Film JPA entity to be mapped to a PagedResources of Film resources. I would suggest not to expose the internal domain directly as API responses. Any change to the internal model might break the contracted API response affecting your API consumers.
-
filmResourceAssembler method argument: an instance of ResourceAssembler.java. pagedResourcesAssembler.toResource() internally executes the assembler.toResource() method where assembler is filmResourceAssembler. The Film Resource Assembler.toResource() method implementation uses a MapStruct-based mapper to map the domain entity Film to the API resource FilmResource. pagedResourcesAssembler.toResource() method implementation also adds a PageResource with page metadata and the pagination links to the RESTful response.
3.3. RESOURCE ASSEMBLER
ResourceAssembler.java
:
You need instances of this class because pagedResourcesAssembler.toResource(), found in the REST controller implementation, takes a ResourceAssembler method argument. The library internally executes the overriden method toResource().
Instances of this class gets injected a MapStruct-based mapper toResource() method uses to map from the internal domain model to the response model.
ResourceAssemblerConfig.java
:
This configuration class instantiates the filmResourceAssembler bean. Notice how you could have used the generic Java class ResourceAssembler.java to instantiate other resource assemblers.
Maybe an actorResourceAssembler:
3.4. JPA AND REST RESOURCE MODELS
Film.java
:
FilmResource.java
:
The domain and response models along with the explanation for Identifiable interface and ResourceSupport base class were covered while discussing Adding HAL Links to Spring Boot applications using Spring HATEOAS.
3.5. RESOURCE MAPPER
FilmResourceMapper.java
:
This is a MapStruct-based mapper that maps the internal domain model Film to the API response FilmResource.
It adds a self links to the film resource and also adds a link to each actor resource that performed in the movie.
4. SAMPLE RESPONSE PAYLOADS
- An
application/json
example:
The outer links array in the response payload includes references to the first, previous, current (via "rel" : "self"
), next, and last resources pages.
The content array includes all Film resources for this page. Each resource includes a nested links array with a reference to itself and an array of references to the film actors via the "rel" : "actors"
objects.
The page object provides metadata about the number of pages, current page, data set size, etc.
- The same request but accepting
application/hal+json
:
This is a HAL-formatted response payload example.
The page object is like the one included in the previous example.
The outer _links object includes the navigational references to other pages.
And the _embedded object adds the films. Each resource includes a nested _links with references related to its owning entity.
You could replace the links’ hostname and port with those of an Edge server or load balancer or Gateway via the X-FORWARDED-*` headers.
5. CONCLUSION
One of the approaches to navigate through a data set is through pagination. Including HAL links in your REST API responses hints client applications what they could do next. Some of the next action a user might want to do is to navigate to the next page to book a room in a different hotel than those found in the first page.
Spring HATEOAS helps adding navigation links and page metadata. Such links and metadata in your API responses allow client applications to improve the user experience.
This tutorial helps you getting started with spring-boot-starter-hateoas
Spring Boot starter to include pagination hypermedia in your RESTful API responses.
6. UPCOMING
Lets take a quick look at the Hibernate logs these sample requests generate:
Aparently there is nothing wrong here, a SQL query to retrieve film records. Notice the limit
and offset
Hibernate generates for the H2 engine. It then executes a second query, a count
SQL statement used to provide page metadata.
But Hibernate also generates these queries:
-
One SQL query to retrieve a language.
Hibernate executed only one SQL statement because the five films retrieved are in English, otherwise it would have generated one SQL query for each different language. -
Many SQL queries to retrieve the actors for each film.
This is known as the N+1 SELECT problem. Stay tuned, I’ll cover how to prevent the N+1 SELECT problem using Spring Data JPA in a follow-up post.
Thanks for reading and as always, feedback is very much appreciated. If you found this post helpful and would like to receive updates when content like this gets published, sign up to the newsletter.
7. SOURCE CODE
Accompanying source code for this blog post can be found at: