Pagination and sorting for REST interface with Spring

June 19, 2017

Spring Data Rest has the ability to generate a CRUD REST layer based on Spring Data Repositories and their entities. This works quite nice and also includes an approach for pagination and sorting of the results. When the automatically created interface does not fit your needs and you are not able to configure it in the way you want it might makes sense to retain the pagination approach in manual implementation.

flavor wheel

When a list of resources is requested (e.g. a list of all accounts), Spring Data Rest allows to append some pre-defined query parameters to the http request. E.g. to get the page with index 3 and a page size of 15 the request would look like this:

curl -X GET /accounts?page=3&size=15

and the result could look like this:

{
  "content": [
    {
      "firstName": "Arne",
      "lastName": "Bosien"
    } ,
    {
      //...
    }
  ],
  "totalElements": 147
}

Many times pagination is not sufficient but needs to be combined with sorting. Spring Data Rest implements that with this kind of request:

curl -X GET /accounts?page=3&size=15&sort=firstName,ASC

The structure of the result will look the same like before but with different data.

In order to get this functionality into a self-written REST layer and to Spring Data repositories there is not much to do. Basically, in the REST classes a Pageable has to be added into the signature and the return type needs to be changed to be a Page with according type parameter. E.g.:

@GetMapping("/accounts")
public Page<AccountDto> getAll(
    @PageableDefault(size = 20) Pageable pageable
) {
    return accountService.getAll(pageable);
}

The @PageableDefault annotation is not required but very handy. A simple implementation of the AccountService with an AccountRepository that extends the PagingAndSortingRepository could look like this:

public Page<AccountDto> getAll(Pageable pageable) {
    return accountRepository
        .findAll(pageable)
        .map(account -> new AccountDto(
                account.getFirstName(), 
                account.getLastName()
        ));
}

In case you need a different implementation that requires the explicit creation of the returned Page the following snippet would work:

public Page<AccountDto> getAll(Pageable pageable) {
    Page<Account> page = accountRepository.findAll(pageable);
    List<AccountDto> dtos = myConversion(page);
    return new PageImpl<>(dtos, pageable, page.getTotalElements())
}