REST pagination with Spring MVC and JPA keeping your Controllers Clean

Marcos Abel
Trabe
Published in
4 min readNov 30, 2020

There are plenty of solutions out there to implement pagination in REST APIs. This story is not about best practices or the solution I personally like the most. It is, instead, about how to implement one of the commonly used alternatives as cleanly as possible using Spring Boot and JPA.

The pagination alternative covered in this story uses a couple of URL parameters for pagination and returns a collection of resource items and a header with the total elements in the collection. For a paginated collection of Resource items, the call would look like: GET /api/resources?_start=10&_end=15. Let’s say that our resource has an id and a name. The expected result for the GET above would be something like this:

X-Total-Count: 2000
[{"id":100, "name": "Element 100"},{"id":101, "name": "Element 101"},{"id":102, "name": "Element 102"},{"id":103, "name": "Element 103"},{"id":104, "name": "Element 104"}]

The names of the parameters are not relevant or the name of the header can be different across implementations. There is a slightly different variant using page and elements as parameters but the concept is the same: we request a page of a collection of resources and we want the list of elements contained in the page and a header with the total size of our collection.

Implementing this kind of operation using Spring and JPA

For the repository, we can use a JpaRepository and write something like this:

We assume here that our application uses layer-specific POJOs, so our service would call the appropriate operation of the repository and convert the result before returning:

That convert method is obviously not implemented in the snippet above. We could use MapStruct to convert between POJOs but the details of the implementation of the model are not relevant to the solution. The only thing we need to know to understand the rest of the story is that our services would receive a Pageable object as a parameter and would return a Page as a result.

Resolve the Pageable object from the request parameters

In order to reduce the amount of code in our controllers, we can use an argument resolver to create a Pageable object using the parameters from the request. I wrote an article about how to inject custom parameters a while back. You can check it out for specifics, but our resolver would look like this:

We need to add the resolver to our application resolvers:

With this configuration in place, we can write our controller method as follows:

With the argument resolver, we have made the pageable object automagically available to our controller methods. This is very good because now we don’t have any code to handle the creation of the Pageable object lying around in our controllers.

Return the expected format

The problem with the code above is that it doesn’t generate the response described at the beginning of this story. The list of elements looks ok but we are not generating the X-Total-Count header.

A trivial solution to our problem would be this one:

The problem with this solution is that we pollute our controller with the generation of the header…and, if we have multiple methods returning paginated collections, we are going to repeat this over and over again.

Luckily for us, Spring MVC provides a tool that allows us to customize the response of our rest controllers before the response is written back to the client using HttpMessageConverter. This tool is called ResponseBodyAdvice. The interface looks like this:

It is similar, in a way, to HandlerMethodArgumentResolver. We have a supports method where we decide if we support a given parameter type and then beforeBodyWrite where we can customize our response. Let’s go back to our controller and just return a Page:

Now we need to implement our ResponseBodyAdvice:

Sadly, ResponseBodyAdvice is not designed to be parametrized with an input type and an output type. When we declare a ResponseBodyAdvice<T>the T defines the type of the parameter. We will receive the parameter as the first parameter of our beforeBodyWrite method and we will return the new value after the customization.

We need to implementResponseBodyAdvice<Object> and perform some ugly casts to have the job done…but it works.

Wrapping up

This story is about a very specific problem but the solution is implemented using a very generic mechanism. It’s a pity that Spring doesn’t provide a ResponseBodyAdvice<T,K> to make the implementation cleaner but, accepting a couple of old-school casts in our code-base, we can have really clean controllers for this kind of APIs.

--

--