javaspring-bootreactive-programmingspring-webfluxwebflux

Unable to return a request within webflux in reactive manner


Here is the following flow in my code: User calls create agency endpoint, containing the agency name and a boolean field ->

Router functions receives call, passes to handler ->

Handler converts request body to Mono, passes that mono so a service ->

Service saves agency to DB, generating an ID in the process, then creates a Response object containing the ID wrapped in a Mono, returns Response back to the Handler.

This is the part where I am stuck. Maintaining a reactive approach, I must save the Agency to Db and create the ID within a "doOnNext() part of the Mono. However, I can't return the ID that I get from the DB in order to create the response object.

What is the reactive way to accomplish this? Thanks in advance!

public RouterFunction<ServerResponse> createAgency() {
        return RouterFunctions
                .route(POST("/agency").and(accept(MediaType.APPLICATION_JSON)), handler::createAgency);
    }
@Component
@AllArgsConstructor
public class AgencyHandler {

    private final AgencyServiceImpl service;

    @NonNull
    public Mono<ServerResponse> createAgency(ServerRequest request){
        Mono<CreateAgencyRequest> agency = request.bodyToMono(CreateAgencyRequest.class);

        Mono<CreateAgencyResponse> response = service.createAgency(agency);

        return ServerResponse.created(URI.create("/agency"))
                .contentType(MediaType.APPLICATION_JSON)
                .body(response, CreateAgencyResponse.class);
    }
}
@Service
@AllArgsConstructor
public class AgencyServiceImpl implements AgencyService {
    private final AgencyRepository agencyRepository;
    private final UuidGenerator uuidGenerator;

    public Mono<CreateAgencyResponse> createAgency(Mono<CreateAgencyRequest> request) {
        UUID id;
        request.doOnNext(agency -> {
            UUID agencyId = uuidGenerator.getUUID();
            Mono<Agency> newAgency = Mono.just(
                new Agency(
                    agencyId,
                    agency.getFields().getName()
            ));

            //repository.save(newAgency)

            //return Mono.just(new CreateAgencyResponse(new CreateAgencyResponseData(agencyId.toString())));
        });

        // return request
        return Mono.just(new CreateAgencyResponse(new CreateAgencyResponseData(agencyId.toString())));
    }
}

Solution

  • Something like the following should do the trick:

    @Service
    @AllArgsConstructor
    public class AgencyServiceImpl implements AgencyService {
        private final AgencyRepository agencyRepository;
        private final UuidGenerator uuidGenerator;
    
        public Mono<CreateAgencyResponse> createAgency(Mono<CreateAgencyRequest> request) {
            UUID agencyId = uuidGenerator.getUUID();
    
            return request.flatMap(createAgencyRequest -> {
                Agency agency = new Agency(agencyId, agency.getFields().getName();
                return repository.save(newAgency);    
            }).map(saved ->
                new CreateAgencyResponse(new CreateAgencyResponseData(agencyId.toString()));
            )
        }
    }
    

    You would create the Agency in the flatMap operation and store it in the database. I assume your repository is also reactive so it should return Mono as well, hence the flatMap operation. Afterwards you just need to map whatever the repository returned (you may want to have some logic here based on the successful operation on the database) to create the CreateAgencyResponse.