Spring Webflux Pagination

Pagination in Spring Webflux and Spring Data Reactive is a technique used to efficiently retrieve and display large sets of data in a non-blocking and reactive manner. Spring Webflux is a framework that supports reactive programming in the Spring ecosystem, while Spring Data Reactive provides support for reactive data access.

To implement pagination in Spring Webflux, you can use the Pageable interface, which allows you to specify the page number, page size, and sorting criteria for your data. Spring Data Reactive provides repositories that support reactive data access and can be easily integrated with Spring Webflux.

When a request for paginated data is made, Spring Webflux and Spring Data Reactive work together to retrieve and return a subset of the data, reducing the memory and processing resources required. This approach is especially useful for handling large datasets or when building responsive web applications where blocking operations can lead to poor performance.

1. Introduction

Spring WebFlux is a reactive programming framework within the broader Spring ecosystem for building asynchronous, non-blocking, and event-driven web applications. It was introduced in Spring Framework 5 to provide an alternative to the traditional Spring MVC framework for building web applications. Spring WebFlux is particularly well-suited for building highly scalable and responsive applications that can handle a large number of concurrent connections.

  • Reactive Programming Model: Spring WebFlux is built on the principles of reactive programming, which allows developers to handle asynchronous and event-driven operations using reactive streams. It uses Project Reactor, a reactive programming library, to provide the necessary building blocks for reactive applications.
  • Non-Blocking: Spring WebFlux is non-blocking, meaning it can handle a large number of concurrent requests without blocking threads. This is particularly useful for applications that require high concurrency and low-latency responses.
  • Annotation-Based Controllers: Spring WebFlux supports annotation-based controllers for defining endpoints and handling HTTP requests.
  • Functional Endpoints: In addition to annotation-based controllers, Spring WebFlux also allows you to define endpoints using a more practical and programmatic approach. This can be useful for building reactive APIs.
  • Router Functions: Router functions are a fundamental concept in Spring WebFlux for defining how incoming requests are routed to handlers. They provide a way to create custom routing logic, allowing for more flexibility in defining endpoints.
  • Support for Multiple Reactive Data Sources: Spring WebFlux is not limited to HTTP requests. It can also handle other types of data sources, such as WebSockets, server-sent events (SSE), and more. This makes it suitable for building real-time applications.
  • Reactive WebClient: Spring WebFlux includes a reactive WebClient that allows you to make asynchronous HTTP requests to external services. It integrates seamlessly with the reactive programming model.
  • Adaptive Threading: Spring WebFlux uses an adaptive threading model that automatically adjusts the number of threads based on the workload, making efficient use of system resources.
  • Backpressure Support: Reactive streams, used in Spring WebFlux, support backpressure, which allows consumers to signal producers about their ability to handle data. This helps prevent overwhelming the system with too much data.
  • Integration with Spring Ecosystem: Spring WebFlux can be integrated with other Spring projects and libraries, such as Spring Security, Spring Data, and Spring Cloud, making it suitable for building comprehensive microservices architectures.
  • Annotated Controllers for MVC: While Spring WebFlux is primarily designed for reactive applications, it also includes the option to use annotated controllers in a traditional Spring MVC style, allowing you to mix both programming models in a single application.
  • Functional Error Handling: Spring WebFlux provides functional error handling mechanisms that allow you to define how errors are handled at different levels of the application, including global error handling.
  • Reactive Security: Spring Security can be integrated with Spring WebFlux to provide security features for reactive applications, including authentication and authorization.

2. Significance of Pagination

Pagination is a common technique used in user interfaces and web applications to break down large sets of data into smaller, more manageable chunks or pages. Its significance lies in several key aspects:

  • Improved User Experience: Pagination enhances the user experience by preventing information overload. Users can easily navigate through data in smaller increments, which is particularly important when dealing with extensive datasets. This prevents long scrolling and allows users to find relevant content more quickly.
  • Reduced Load Time: Loading a large amount of data all at once can significantly slow down a web page or application. Pagination reduces the initial load time by fetching and displaying only a limited portion of the data at a time. This results in faster page rendering and improved overall performance.
  • Lower Bandwidth Usage: Transmitting large datasets over the internet consumes more bandwidth. By paginating data, you reduce the amount of data transmitted with each request, which can be particularly important for users with limited bandwidth or in situations where bandwidth costs are a concern.
  • Optimized Server Resources: Pagination helps distribute the server’s workload more evenly. Instead of serving an entire dataset in one go, the server can handle smaller, more manageable requests. This prevents resource exhaustion and maintains system responsiveness, especially in high-traffic applications.
  • Easier Navigation: Users can easily navigate to a specific page or section of data using pagination controls like “Next,” “Previous,” or page numbers. This provides a clear and intuitive way to access different parts of the dataset.
  • Search Engine Optimization (SEO): For websites with a lot of content, pagination can help with SEO. It allows search engines to crawl and index individual pages, making it easier for users to find specific content through search results.
  • Enhanced Mobile Usability: On mobile devices with smaller screens, pagination can be particularly important. Scrolling through a long list on a small screen can be cumbersome, so breaking content into pages provides a more user-friendly experience.
  • Customization and Sorting: Pagination often allows users to customize the number of items displayed per page and to sort data based on different criteria (e.g., date, relevance, popularity). This flexibility empowers users to tailor their experience to their preferences.
  • Data Privacy: In some applications, such as e-commerce or personal profiles, pagination can help protect user data privacy. It limits the amount of information that can be viewed at once, reducing the risk of exposing sensitive data.
  • Accessibility: Pagination controls can be made accessible to users with disabilities, ensuring that everyone, regardless of their abilities, can navigate and access the content effectively.

2.1 Pagination in Spring Data vs. Spring Data Reactive

Pagination in Spring Data and Spring Data Reactive serves the same fundamental purpose of breaking down large datasets into manageable chunks for efficient retrieval. However, they differ in their approach due to the synchronous and asynchronous nature of their underlying data access methods.

2.1.1 Spring Data (Synchronous)

  • Blocking Operations: In Spring Data’s traditional, synchronous approach, pagination is typically performed using methods that return a Page or Slice object. These methods block the execution thread until the data retrieval is complete, which means they are suitable for synchronous, blocking applications.
  • Efficiency for Small Datasets: Spring Data’s pagination is well-suited for scenarios where the dataset is not overly large, and the overhead of managing reactive streams is not required. It provides a convenient way to retrieve data with minimal complexity.
  • Ease of Use: Spring Data’s pagination methods are straightforward to use and integrate into your application, especially if you are already familiar with Spring Data.
  • Limited Concurrency: The synchronous approach may not be the best choice for applications that require high concurrency or need to handle a large number of concurrent requests efficiently. It might lead to thread contention and performance bottlenecks.

2.1.2 Spring Data Reactive (Asynchronous)

  • Reactive Streams: Spring Data Reactive, on the other hand, leverages reactive programming principles and uses reactive data access methods, such as Flux or Mono. These methods return reactive types that represent streams of data. They are non-blocking and suitable for building asynchronous, event-driven applications.
  • Efficiency for Large Datasets: Spring Data Reactive shines when dealing with large datasets or scenarios where high concurrency is crucial. It efficiently manages resources and can handle many concurrent requests without blocking threads.
  • Scalability: Reactive pagination is well-suited for microservices architectures and applications that need to scale easily to handle varying workloads.
  • Complexity: Using reactive data access methods may introduce a learning curve, especially if you are new to reactive programming concepts. However, once you become familiar with these concepts, they provide powerful tools for building responsive and scalable applications.
  • Integration with Spring WebFlux: Spring Data Reactive integrates seamlessly with Spring WebFlux, allowing you to build end-to-end reactive applications that handle asynchronous data retrieval, processing, and presentation.

The choice between Spring Data and Spring Data Reactive for pagination depends on your application’s requirements and the nature of your data access layer. If your application is primarily synchronous and doesn’t require high concurrency, Spring Data’s traditional approach is straightforward to use. On the other hand, if you are building reactive, asynchronous, or highly concurrent applications, Spring Data Reactive offers the necessary tools for efficient data retrieval and processing.

3. Setting up Mongodb on Docker

In the present world, Docker is an important term. It is often used in CI/CD platforms that package and run applications along with their dependencies inside a container. Docker has become a standard for Linux Containers. A *container* is a runtime environment that operates under any Linux kernel, providing a private, isolated space similar to that of a standalone machine within the Linux operating system. If someone needs to go through the Docker installation, please watch this video.

To set up the Mongodb I will be making use of Docker and for that, I have prepared a simple docker-compose.yml that will help to set up the mongodb with a default database as – reactive


    container_name: mongodb
    image: mongo
      - "27017:27017"
version: "3"

To get the mongodb up and running we will trigger the following command docker-compose -f /stack.yml up -d. If the images are not present in the host environment then they will be downloaded from the Dockerhub repository and the whole process might take a minute or two. Once done you can use the docker ps command to confirm whether the container is running or not as shown in the below image.

Spring Webflux Pagination
Fig. 1: Mongodb on Docker

You can also use the following command docker-compose -f /stack.yml up -d to clean up the created environment.

3.1 Establishing Mock Data

This script demonstrates how to work with MongoDB within a Docker container. It covers database setup, collection creation, insertion of dummy data, and basic data retrieval. Ensure you have the MongoDB container (mongodb) running before executing these commands.


# Step 1: Enter the MongoDB container
docker exec -it mongodb mongosh

# Step 2: Use the "reactive" database
use reactive;

# Step 3: Create an empty collection named "tasks"

# Step 4: Insert some dummy documents into the "tasks" collection
(() => {
    var dummyData = [];
    for (var i = 1; i <= 50; i++) {
        var completedStatus = i % 2 === 0;
            id: i,
            description: "Task " + i,
            completed: completedStatus

# Step 5: Count the number of documents in the "tasks" collection

# Step 6: View the documents in the "tasks" collection

4. Developing a Spring Webflux application

Let’s delve into some practical exercises. To initiate Pagination in Spring Webflux, you can proceed with the following steps. Make sure that your Mongo server is up and running before configuring the application.

4.1 Incorporating Dependencies into pom.xml

Establish a fresh Spring Boot project using Spring Initialzr or leverage an existing one. Include the necessary dependencies in your project’s pom.xml file.


    <!-- Spring Boot Starter for MongoDB Reactive -->

    <!-- Spring Boot Starter for WebFlux -->

    <!-- Spring Boot Starter for Testing -->

    <!-- Project Reactor Test Library for Testing -->

4.2 Configure Application Properties

Let us understand the application.properties.

  • Spring Configuration:
    • server.port=9030: This property sets the port on which the Spring Boot application will run. In this case, the application will listen on port 9030.
    • spring.application.name=spring-webflux-reactive-pagination: This property sets the name of the Spring Boot application. It’s a user-defined name for the application, and it can be used for various purposes, including identifying the application in a distributed system.
    • spring.main.banner-mode=off: This property turns off the application’s startup banner. The banner is the ASCII art or text that is displayed in the console when the application starts. Setting it to “off” disables the banner.
  • MongoDB Configuration:
    • spring.data.mongodb.uri=mongodb://localhost:27017/reactive: This property configures the connection to a MongoDB database. It specifies the URI of the MongoDB instance to connect to. In this case:
      • mongodb://: Indicates the MongoDB protocol.
      • localhost:27017: Specifies the hostname and port number where MongoDB is running. In this example, MongoDB is assumed to be running locally on the default port 27017.
      • /reactive: Specifies the name of the MongoDB database to connect to, which is named “reactive” in this case.





4.3 Create Model Entity Class

Todo is a model class used for defining entities in a MongoDB database. It represents a document in the “tasks” collection. Here’s a breakdown of the class:

  • @Document(collection = "tasks"): This annotation from Spring Data MongoDB specifies that instances of this class are MongoDB documents, and they should be stored in the “tasks” collection within the MongoDB database.
  • @Id: This annotation marks the _id field as the primary identifier for documents in the collection. MongoDB automatically generates unique identifiers for documents unless one is provided.
  • private ObjectId _id;: This field represents the unique identifier for the document. It corresponds to the _id field in MongoDB.
  • private int id;: This field represents a custom ID for the document. It’s used to store an integer identifier for the task.
  • private String description;: This field stores the description or content of the task.
  • private boolean completed;: This field stores a boolean value indicating whether the task is completed or not.
  • The class also provides getters and setters for all fields, allowing access to and modification of the document’s properties.


package com.example.demo.model;

import org.bson.types.ObjectId;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;

// Mongodb entity class

@Document(collection = "tasks")
public class Todo {

    private ObjectId _id;
    private int id;
    private String description;
    private boolean completed;

    public ObjectId get_id() {
        return _id;

    public void set_id(ObjectId _id) {
        this._id = _id;

    public int getId() {
        return id;

    public void setId(int id) {
        this.id = id;

    public String getDescription() {
        return description;

    public void setDescription(String description) {
        this.description = description;

    public boolean isCompleted() {
        return completed;

    public void setCompleted(boolean completed) {
        this.completed = completed;

    public String toString() {
        return "Todo{" +
                "id=" + id +
                ", description='" + description + '\'' +
                ", completed=" + completed +

4.4 Create Repository JPA Interface

The interface, TodoRepository, is a Spring Data MongoDB repository interface used for defining data access operations for the “Todo” entity in a Spring Boot application. Here’s an explanation of the interface:

  • @Repository: This annotation indicates that the interface is a Spring-managed component, specifically a repository. It’s typically applied to classes and interfaces that interact with databases or external data sources.
  • ReactiveMongoRepository<Todo, Integer>: This interface extends ReactiveMongoRepository, which is provided by the Spring Data MongoDB project. It specializes in MongoDB data storage and provides methods for various data operations. It’s parameterized with <Todo, Integer>, where Todo is the entity type this repository deals with, and Integer is the type of the primary key.
  • Flux<Todo> findByCompleted(boolean isCompleted);: This method is used to find and return a Flux of Todo objects where the completed field matches the provided boolean value (isCompleted). It leverages reactive programming concepts, and the result is a reactive stream that emits matching Todo objects.
  • Flux<Todo> findAllBy(Pageable pageable);: This method is used to retrieve a paginated list of Todo objects. The Pageable parameter allows you to specify pagination settings like the desired page number, items per page, sorting criteria, and more. The method returns a Flux of Todo objects, making it suitable for efficient and non-blocking retrieval of paginated data in reactive applications.


package com.example.demo.repository;

import com.example.demo.model.Todo;
import org.springframework.data.domain.Pageable;
import org.springframework.data.mongodb.repository.ReactiveMongoRepository;
import org.springframework.stereotype.Repository;
import reactor.core.publisher.Flux;

A ReactiveMongoRepository is an interface provided by the Spring Data MongoDB project for building reactive data access operations in a Spring Boot application. It extends the ReactiveCrudRepository interface and specializes in MongoDB data storage. Developers can define custom repository interfaces by extending ReactiveMongoRepository and defining method signatures for various data operations like saving, finding, updating, and deleting documents. These methods are automatically implemented by Spring Data MongoDB, providing a straightforward way to interact with a MongoDB database using reactive programming concepts, such as Mono and Flux. This repository interface simplifies the development of reactive data access layers in Spring Boot applications, promoting non-blocking and efficient handling of database interactions in reactive web applications and microservices.
public interface TodoRepository extends ReactiveMongoRepository<Todo, Integer> {

    Flux<Todo> findByCompleted(boolean isCompleted);

    The method findAllBy(Pageable pageable) is part of a reactive programming paradigm, specifically associated with a Flux. It is used to retrieve a paginated list of Todo objects. The Pageable parameter allows you to specify the pagination settings, such as the desired page number, the number of items per page, sorting criteria, and more. The method returns a Flux of Todo objects, meaning it asynchronously emits these objects in a reactive stream, enabling efficient and non-blocking retrieval of paginated data. This is commonly used in scenarios where you want to retrieve a subset of Todo records, such as when implementing paginated APIs or database queries in reactive applications.
    Flux<Todo> findAllBy(Pageable pageable);

4.5 Create Service Class

The class, TodoService, is a Spring service class responsible for handling business logic related to the `Todo` entity in a reactive Spring Boot application. Here’s an explanation of the class and its methods:

  • @Service: This annotation indicates that the class is a Spring-managed service component, typically used to encapsulate business logic and coordinate interactions between controllers and repositories.
  • @Autowired: This annotation injects an instance of the TodoRepository interface into the repository field, allowing the service to interact with the data repository.
  • public Flux findAll(): This method retrieves all Todo objects from the repository using a reactive Flux. It returns a stream of Todo objects.
  • public Flux findByCompletedFlag(boolean isCompleted): This method retrieves Todo objects with a specified completion status (isCompleted) from the repository. It returns a stream of matching Todo objects.
  • public Mono<Page> findAllBy(Pageable pageable): This method is responsible for retrieving a paginated list of Todo objects from the data repository using Spring Data’s reactive features. It uses a series of reactive operators to achieve this:
    • It queries the data repository using repository.findAllBy(pageable).
    • It collects all the retrieved Todo objects into a Mono<List>.
    • It combines this Mono<List> with a Mono representing the total count of Todo objects.
    • Finally, it maps the result into a Mono<Page> by constructing a PageImpl object, which represents paginated results.


package com.example.demo.service;

import com.example.demo.model.Todo;
import com.example.demo.repository.TodoRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

public class TodoService {

    TodoRepository repository;

    public Flux<Todo> findAll() {
        return repository.findAll();

    public Flux<Todo> findByCompletedFlag(boolean isCompleted) {
        return repository.findByCompleted(isCompleted);

    This method is responsible for retrieving a paginated list of Todo objects from a data repository using Spring Data's reactive features. Here's a breakdown of the method:

    1) repository.findAllBy(pageable): This part of the code queries the data repository (repository) to retrieve a list of Todo objects based on the provided pageable parameter. The findAllBy method likely uses Spring Data MongoDB's built-in query capabilities to retrieve data in a pageable manner.

    2) .collectList(): This part of the code collects all the retrieved Todo objects into a Mono<List<Todo>>. The result is a Mono that emits a single list of Todo objects.

    3) .zipWith(repository.count()): In this step, the code combines the previously obtained Mono<List<Todo>> (from collectList) with another Mono<Long>, which represents the count of all Todo objects in the repository. The zipWith operator is used to combine the two Mono instances into a new Mono<Tuple2<List<Todo>, Long>>. This tuple contains both the list of Todo objects and the total count.

    4) .map(objects -> new PageImpl<>(objects.getT1(), pageable, objects.getT2())): Finally, the code maps the Tuple2 containing the list of Todo objects and the count into a Mono<Page<Todo>>. It constructs a PageImpl object, which is a Spring Data class used to represent paginated data. The PageImpl is initialized with the list of Todo objects (objects.getT1()), the provided pageable, and the total count (objects.getT2()). This resulting Mono emits a Page object representing the paginated results.
    public Mono<Page<Todo>> findAllBy(Pageable pageable) {
        return repository.findAllBy(pageable)
                .map(objects -> new PageImpl<>(objects.getT1(), pageable, objects.getT2()));

4.6 Create Controller Class

The class, TodoReactiveController, is a Spring REST controller class responsible for handling HTTP requests related to the Todo entity in a reactive Spring Boot application.


package com.example.demo.controller;

import com.example.demo.model.Todo;
import com.example.demo.service.TodoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

public class TodoReactiveController {

    TodoService service;

    // http://localhost:9030/api/todos
    public Flux<Todo> getAllTodos() {
        return service.findAll();

    // http://localhost:9030/api/todos/completed?completed=true
    public Flux<Todo> findByCompleted(@RequestParam(name = "completed", defaultValue = "false") boolean completed) {
        return service.findByCompletedFlag(completed);

    // http://localhost:9030/api/todos/pageable?page=0&size=10
    public Mono<Page<Todo>> findAllBy(@RequestParam(name = "page", defaultValue = "0") int page,
                                      @RequestParam(name = "size", defaultValue = "10") int size) {
        The given code initializes a Pageable object in Java using the PageRequest.of(page, size) method. The Pageable interface is used for managing paging and pagination, allowing you to specify parameters like page number and items per page. PageRequest is a concrete implementation of Pageable, and of(page, size) is a static factory method within PageRequest used to create a Pageable object with specific page and size values. This Pageable object is typically employed in data retrieval tasks, enabling developers to request a particular page of data from a larger dataset, commonly in the context of database queries or API operations.
        Pageable pageable = PageRequest.of(page, size);
        return service.findAllBy(pageable);

4.7 Create Implementation Class

The DemoApplication class serves as the entry point for a Spring Boot application. It’s marked with the @SpringBootApplication annotation, which combines key Spring annotations like @Configuration, @EnableAutoConfiguration, and @ComponentScan, streamlining the configuration setup. The @EnableWebFlux annotation in a Spring Boot application highlights that this annotation activates and configures the WebFlux framework, which is a reactive programming paradigm for building web applications. WebFlux allows developers to use reactive constructs like Flux and Mono for handling asynchronous and non-blocking operations.


package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.reactive.config.EnableWebFlux;

// Springboot application with mongodb

The @EnableWebFlux annotation in Spring Boot is used to activate and configure the WebFlux framework, a reactive programming paradigm for building web applications. By annotating the main application class with @EnableWebFlux, developers enable the use of reactive constructs like Flux and Mono for handling asynchronous and non-blocking operations. WebFlux is particularly beneficial in scenarios requiring high concurrency and low-latency responses, making it suitable for real-time applications and microservices. This annotation empowers developers to leverage reactive programming principles to efficiently manage I/O-bound operations, ultimately enabling the creation of scalable and responsive web applications.
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);

4.8 Run the Application

Now you can run your Spring Boot application. You can test the different endpoints using tools like curl or a REST client like the Postman tool.

Curl Requests

-- get all todos
curl -X GET http://localhost:9030/api/todos

-- get todos by completed flag
curl -X GET http://localhost:9030/api/todos/completed?completed=true

-- get todos by pagination
curl -X GET http://localhost:9030/api/todos/pageable?page=0&size=10

5. Conclusion

In conclusion, the choice between “Pagination in Spring Data” and “Pagination in Spring Data Reactive” depends on the specific requirements and characteristics of your application.

If you are developing a traditional, synchronous application with relatively low concurrency and straightforward database interaction, “Pagination in Spring Data” might be a more straightforward and practical choice. It provides a simple and effective way to paginate through large datasets, and it seamlessly integrates with the broader Spring ecosystem, offering a wide range of features and customization options.

On the other hand, if you are working on a highly concurrent and responsive application that deals with a substantial number of real-time events or requires non-blocking I/O operations, “Pagination in Spring Data Reactive” using Spring WebFlux can be a valuable option. It embraces reactive programming principles, allowing your application to efficiently handle asynchronous operations and high levels of concurrency while providing pagination capabilities. It’s particularly well-suited for scenarios where responsiveness and scalability are paramount.

Ultimately, the choice between the two approaches should align with your project’s specific goals and constraints. Both “Pagination in Spring Data” and “Pagination in Spring Data Reactive” offer robust solutions for managing large datasets, and your decision should be based on factors such as application architecture, performance requirements, and the need for reactive features. By making an informed choice, you can ensure that your application’s pagination strategy effectively meets your application’s unique needs.

6. Download the Project

This was a tutorial to understand the pagination in spring flux. You can download the source code from the below link.

You can download the full source code of this example here: Pagination in Spring Webflux and Spring Data Reactive


An experience full-stack engineer well versed with Core Java, Spring/Springboot, MVC, Security, AOP, Frontend (Angular & React), and cloud technologies (such as AWS, GCP, Jenkins, Docker, K8).
Notify of

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Inline Feedbacks
View all comments
Back to top button