I'm using Spring Webflux & reactor, Java 11, Spring boot 2.4.5, Spring 5.3.6 versions for this reactive application.
Use case:
I need to make a call to API and get data from it. From this data I take uniqueId and then call bunch of API's to get other data, and then finally combine all this data to new Object and return.
Example code:
Mono<Response> 1stAPIResponse = 1stAPI.callMethod(eventId); // response object has ProductId, and other details.
Mono<List<String>> productIds = 1stAPIResponse.map(Response::ProductId).collect(Collectors.toList());
Mono<2ndAPIResponse> 2ndAPIResponse = productIds.flatMap(ids -> 2ndAPI.callMethod(ids));
Mono<3rdAPIResponse> 3rdAPIResponse = productIds.flatMap(ids -> 3rdAPI.callMethod(ids));
...
1stAPIResponse.foreach(response -> {
FinalResponse.builder()
.productId(response.productId)
.val1(2ndAPIResponse.get(response.productId))
.val3(3ndAPIResponse.get(response.productId))
. ...
.build()});
Here the problem is, when ids are passed to 2ndAPI, 3rdAPI,... method, it makes call to 1stAPI and get the data each time. And finally when creating object it makes another call to 1st API. In this example it makes total of 3 calls.
How can I avoid similar multiple calls from occurring?
One way to avoid this is, I can make 1stAPI call blocking but is it correct? doesn't it defeat non-blocking style of coding?
Ex: Response 1stAPIResponse = 1stAPI.callMethod(eventId).toFuture().get();
How can I write a correct reactive program (without blocking) but still make only one call to 1stAPI?
Let me know for any questions.
So, you need to refactor your code in more reactive style and use zip operator for parallel calls:
1stAPI.callMethod(eventId)
.flatmap(response -> // collect your id to list (ids);
return 2ndAPI.callMethod(ids).zipWith(3ndAPI.callMethod(ids))
.flatmap(tuple2 -> FinalResponse.builder() // tuple contains result of 2ndAPI and 3ndAPI
.productId(response.productId)
.val1(2ndAPIResponse.get(response.productId))
.val3(3ndAPIResponse.get(response.productId)))
...
)