DEV Community ๐Ÿ‘ฉโ€๐Ÿ’ป๐Ÿ‘จโ€๐Ÿ’ป

DEV Community ๐Ÿ‘ฉโ€๐Ÿ’ป๐Ÿ‘จโ€๐Ÿ’ป is a community of 967,611 amazing developers

We're a place where coders share, stay up-to-date and grow their careers.

Create account Log in
Thomas
Thomas

Posted on • Updated on • Originally published at bootify.io

Pagination in a Spring Boot REST API

Spring Boot provides many features to support Pagination natively. However, the PageImpl class is not directly usable with a RestTemplate and also creates a lot of overhead when outputting via a REST API. Therefore in this article we would like to introduce a SimplePage class that simplifies the JSON structure and can be used directly in a RestTemplate or TestRestTemplate.

Adding Pagination to the Application

To add pagination to a RestController we first need the input parameters. Using the Pageable class, the "page", "size" and "sort" parameters can be passed to the endpoint. These are automatically bound by Spring Boot into the object. Default values can be specified by adding annotations or adjusting certain application properties.

@RestController
@RequestMapping(value = "/api/roles", produces = MediaType.APPLICATION_JSON_VALUE)
public class RoleController {

    // ...

    @GetMapping
    public ResponseEntity<SimplePage<RoleDTO>> getAllRoles(
            @SortDefault(sort = "priRole") @PageableDefault(size = 20) final Pageable pageable) {
        return ResponseEntity.ok(roleService.findAll(pageable));
    }

}
Enter fullscreen mode Exit fullscreen mode

ย  Example RestController with an endpoint supporting pagination

Since our repository extends JpaRepository, we can pass our pageable object directly to the findAll() method. The repository will return an object of type Page containing the requested page and its content.

public interface RoleRepository extends JpaRepository<Role, String> {
}
Enter fullscreen mode Exit fullscreen mode

ย  Our repository providing paged access by default

However, if we were to return Page (or PageImpl) directly through our endpoint, we would have the disadvantages described at the beginning. Therefore the intermediate service not only maps our entity to the DTO, but also returns the SimplePage class.

@Service
public class RoleService {

    // ...

    public SimplePage<RoleDTO> findAll(final Pageable pageable) {
        final Page<Role> page = roleRepository.findAll(pageable);
        return new SimplePage<>(page.getContent()
                .stream()
                .map(role -> mapToDTO(role, new RoleDTO()))
                .collect(Collectors.toList()),
                page.getTotalElements(), pageable);
    }

}
Enter fullscreen mode Exit fullscreen mode

ย  Example intermediate Service

The SimplePage class with full Jackson support

With our new class we simplify the JSON structure. It excludes unneeded properties from PageImpl and overwrites the sort property so that it matches the input parameter as closely as possible.

The constructor is necessary so that Jackson can initialize the class from an input string. This adds support for serialization and deserialization, so that we can also call our endpoint using a RestTemplate.

@JsonIgnoreProperties({
        "pageable",
        "number",
        "numberOfElements",
        "first",
        "last",
        "empty"
})
public class SimplePage<T> extends PageImpl<T> {

    @JsonCreator
    public SimplePage(@JsonProperty("content") final List<T> content,
            @JsonProperty("totalElements") final long totalElements,
            @JsonProperty("totalPages") final int totalPages,
            @JsonProperty("page") final int page,
            @JsonProperty("size") final int size,
            @JsonProperty("sort") final List<String> sort) {
        super(content, PageRequest.of(page, size, Sort.by(sort.stream()
                .map(el -> el.split(","))
                .map(ar -> new Sort.Order(Sort.Direction.fromString(ar[1]), ar[0]))
                .collect(Collectors.toList()))), totalElements);
    }

    public SimplePage(final List<T> content, final long totalElements, final Pageable pageable) {
        super(content, pageable, totalElements);
    }

    public int getPage() {
        return getNumber();
    }

    @JsonProperty("sort")
    public List<String> getSortList() {
        return getSort().stream()
                .map(order -> order.getProperty() + "," + order.getDirection().name())
                .collect(Collectors.toList());
    }

}
Enter fullscreen mode Exit fullscreen mode

ย  The SimplePage class

The JSON output of our REST API will look like this. SimplePage can be easily customized by expanding the constructor, for example if more fields are required.

{
  "content": [
    {
      "name": "ADMIN"
    }
  ],
  "totalElements": 1,
  "totalPages": 1,
  "page": 0,
  "size": 20,
  "sort": [
    "priRole,ASC"
  ]
}
Enter fullscreen mode Exit fullscreen mode

ย  JSON response of our new endpoint

Bootify helps you to create the first version of your next Spring Boot application including database schema and REST API. Pagination for the REST endpoints is available in the Professional plan, which also includes generation of basic test cases using Testcontainers.

ยป Learn more

Top comments (0)

๐ŸŒš Friends don't let friends browse without dark mode.

Sorry, it's true.