1. Introduction
If you’ve ever been working with Project Reactor and have a need to implement an equivalent of a while (or do-while) loop, then you’ve come to the right place 🙂
In this article, I will show you what exactly the expand() method does in Project Reactor and how it can help you to solve the problem of a while (or rather do-while) loop.
2. Project Reactor Expand in Theory
As the first step, let’s try to understand what exactly the Mono.expand() method do? According to the documentation:
Recursively expand elements into a graph and emit all the resulting element using a breadth-first traversal strategy.
I know, it might sound a bit confusing, but let’s look at the following dependency graph:
As we can see, it presents a hierarchical structure of the parent-child relationships. If we would expand a Mono.just(A)
, then the values would be emitted in the following order: A, B, C, D, E, F, G, H.
This behavior can be used to implement a counterpart of a while loop in Project Reactor.
3. Set Up Example Spring WebFlux Project
With that being done, let’s implement a simple Spring WebFlux project, which will help us to understand everything even better.
We will expose a simple REST endpoint with pagination and use the expand() method to fetch all the results.
Make a real progress thanks to practical examples, exercises, and quizzes.
- 64 written lessons
- 62 quizzes with a total of 269 questions
- Dedicated Facebook Support Group
- 30-DAYS Refund Guarantee
3.1. Imports
As the first step, let’s add the necessary import to the project:
dependencies { implementation("org.springframework.boot:spring-boot-starter-webflux") }
3.2. Expose The Test Endpoint
Nextly, let’s expose the example endpoint:
@RestController @RequestMapping("/api") class ExampleController( private val service: ExampleService ) { data class ExampleDto(val name: String, val nextPageToken: String?) val exampleList = mapOf( "" to ExampleDto("Name-1", "token-2"), "token-2" to ExampleDto("Name-2", "token-3"), "token-3" to ExampleDto("Name-3", "token-4"), "token-4" to ExampleDto("Name-4", "token-5"), "token-5" to ExampleDto("Name-5", null) ) @GetMapping("/example") fun getExample(@RequestParam("token") pageToken: String?): Mono<ExampleDto> = pageToken?.let { token -> Mono.just(exampleList[token]!!) } ?: Mono.empty() }
As we can see, the logic is pretty straightforward.
When the API consumer performs a GET request with an empty String, the endpoint returns the name
and nextPageToken
properties:
{ "name": "Name-1", "nextPageToken": "token-2" }
The nextPageToken
property can be then used to iterate through the entries in the Map. Finally, when the user gets the last record, the nextPageToken
property is null and the following request results in the empty Mono being returned.
as a note, I can add that the Google Calendar API works in a similar manner 🙂
4. Project Reactor While Loop
So at this point, we already have our REST endpoint and it’s time to consume it with a do-while loop in some way.
4.1. Add WebClient Config
As the first step, let’s add the WebClient config:
@Configuration class Configuration { @Bean fun webClient(): WebClient = WebClient.builder() .baseUrl("http://localhost:8080") .build() }
4.2. Create Example Service
Nextly let’s implement the ExampleService
and a performRequest
function:
@Service class ExampleService( private val webClient: WebClient ) { private fun performRequest(token: String?): Mono<ExampleController.ExampleDto> = webClient .get() .uri { builder -> builder .path("/api/example") .queryParamIfPresent("token", Optional.ofNullable(token)) .build() } .retrieve() .bodyToMono(ExampleController.ExampleDto::class.java) }
As can be seen, this function is responsible for performing a GET request to the http://localhost:8080/api/example
endpoint. The token
query parameter is set based on the passed argument (which can be null, as well).
4.3. Make Use Of Mono.expand() Method
Finally, let’s make use of the Project Reactor Mono.expand() method to iterate through the results in a do-while style:
operator fun invoke() { performRequest("") .expand { response -> performRequest(token = response.nextPageToken) } .subscribe(System.out::println) }
Now, when we invoke the above function, the following values will be printed to the output:
ExampleDto(name=Name-1, nextPageToken=token-2) ExampleDto(name=Name-2, nextPageToken=token-3) ExampleDto(name=Name-3, nextPageToken=token-4) ExampleDto(name=Name-4, nextPageToken=token-5) ExampleDto(name=Name-5, nextPageToken=null)
As we can see, the initial Mono has been expanded and the values are published until the endpoint returns the empty Mono.
5. Summary
And that would be all for this article on how to implement a while (do-while) loop in Project Reactor.
As always, you can find the source code right here, in this GitHub repository, and the other articles related to Project Reactor here.
If you would like to add anything, or just simply share your thoughts, then please let me know in the comment section. Have a great week and see you in the next articles 🙂