In this tutorial, we will learn how to create pagination in a Spring Boot REST API to serve our API consumers data effectively.
In previous tutorials, we created a basic REST API with various methods to serve a resource: Lemonade.
When retrieving the entire list of entities, we notice that the dataset can grow significantly. This becomes problematic when dealing with thousands of entities, as response times increase, and memory usage becomes inefficient.
A solution to our problem is to divide the large set of data into smaller, manageable chunks called “pages. It helps efficiently retrieve and display a subset of data at a time, rather than loading the entire dataset all at once, which can be resource-intensive and slow.
This solution is called Pagination and is often used when we are facing a lot of data, for instance when we are browsing products in an online store or posts in a social media platform.
When discussing Pagination, the following components are usually involved:
Even though it may sound complex, the implementation is straightforward. In this tutorial, we will use an in-memory database and only the Spring Boot Starter Web dependency to keep things simple.
You can use Spring Initializr to quickly generate a Spring Boot project with Maven or Gradle. We can use the following configurations:
We will split our application into four layers: Domain, Repository, Service, and UI. Each layer has well-defined responsibilities. For our application, each layer represents a class, but later we will have multiple classes for each layer.
You will receive a ready-to-use project. All the configurations are already written in the build.gradle file, and the ExplainjavaRestApiApplication start class is already created.
In the next step, we will create a package for each layer of our application: Domain, Repository, Service, Controller, and a Utils package for adding a class to pack our response
First, we create a model class. BTW, you can check our tutorial on integrating the Lombok Project to see how to get rid of a lot of boilerplate code from model classes.
public class Lemonade {
private Long id;
private String name;
private String description;
public Lemonade(Long id, String name, String description) {
this.id = id;
this.name = name;
this.description = description;
}
//getters and setters
}
Next, we can add a Repository class. We can initialize some items in the constructor, and then add two methods: one for receiving a subset of items and another one for counting the total number of items from our list.
@Repository
public class LemonadeRepository {
private final List lemonades = new ArrayList<>();
public LemonadeRepository() {
for (long i = 1; i <= 100; i++) {
lemonades.add(new Lemonade(i, "Lemonade: " + i, "Description" + i));
}
}
public List findPaginated(int offset, int limit) {
return lemonades.stream()
.skip(offset)
.limit(limit)
.collect(Collectors.toList());
}
public long count() {
return lemonades.size();
}
}
This time, we are not returning a List of items; instead, we need to create a DTO object to hold information about our list. We can add this class in the utils package.
public class PaginatedResponse {
private List content;
private int page;
private int size;
private long totalElements;
private int totalPages;
public PaginatedResponse(List content, int page, int size, long totalElements, int totalPages) {
this.content = content;
this.page = page;
this.size = size;
this.totalElements = totalElements;
this.totalPages = totalPages;
}
//getters and setters
}
Next, we will work on the service layer. We need our repository to populate the PageResponse.
@Service
public class LemonadeService {
@Autowired
private LemonadeRepository lemonadeRepository;
public PaginatedResponse getPaginatedLemonades(int page, int size) {
int offset = page * size;
List lemonades = lemonadeRepository.findPaginated(offset, size);
long totalElements = lemonadeRepository.count();
int totalPages = (int) Math.ceil((double) totalElements / size);
return new PaginatedResponse<>(lemonades, page, size, totalElements, totalPages);
}
}
The final step is to create the controller. This time, we need to receive two things from our consumers: the current page and the size. We can receive this parameters using the @RequestParam annotation:
@RestController
@RequestMapping("/api/lemonades")
public class LemonadeController {
@Autowired
private LemonadeService lemonadeService;
@GetMapping
public PaginatedResponse getLemonades(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size) {
return lemonadeService.getPaginatedLemonades(page, size);
}
}
Let’s test our pagination using Postman. If you are not sure how to use this tool, we created a tutorial to explain it.
We can pass anything we want as params, for instance we can set a size of 10 and iterate through totalPages to get all elements.
In this tutorial we successfully implemented pagination in a REST API using Spring Boot with an in-memory repository.
Learn advanced concepts, work on real-world projects, and fast-track your journey to becoming a proficient Java developer. Start now and unlock your full potential in the world of Java programming!
Start now and unlock your full potential in the world of Java programming!
The place where you can start your Java journey.
© All Rights Reserved.