How to refactor this GraphQL endpoint to use BatchMapping instead of SchemaMapping?

Viewed 36

I am using Spring for GraphQL to create a small microservice project which consists of 2 apps, a customer service and an order service.

My order service app is running on port 8081 and it contains an OrderData model:

public record OrderData(@Id Integer id, Integer customerId) {}

It also contains an OrderDataRepository interface:

@Repository
public interface OrderDataRepository extends ReactiveCrudRepository<OrderData, Integer> {

    Flux<OrderData> getByCustomerId(Integer customerId);

}

And it exposes a single endpoint

@RestController
@RequestMapping(path = "/api/v1/orders")
public class OrderDataController {

    private final OrderDataRepository orderDataRepository;

    public OrderDataController(OrderDataRepository orderDataRepository) {
        this.orderDataRepository = orderDataRepository;
    }

    @GetMapping
    Flux<OrderData> getByCustomerId(@RequestParam Integer customerId) {
        return orderDataRepository.getByCustomerId(customerId);
    }

}

My customer service app defines the following graphql schema:

type Query {
    customers: [Customer]
    customersByName(name: String): [Customer]
    customerById(id: ID): Customer
}

type Mutation {
    addCustomer(name: String): Customer
}

type Customer {
    id: ID
    name: String
    orders: [Order]
}

type Order {
    id: ID
    customerId: ID
}

And it exposes a few graphql endpoints for querying and mutating customer data, one of which is used to fetch customer orders by using a WebClient to call the endpoint exposed by my order service app:

@Controller
public class CustomerController {

    private final CustomerRepository customerRepository;
    private final WebClient webClient;

    public CustomerController(CustomerRepository customerRepository, WebClient.Builder webClientBuilder) {
        this.customerRepository = customerRepository;
        this.webClient = webClientBuilder.baseUrl("http://localhost:8081").build();
    }

    // ...

    @QueryMapping
    Mono<Customer> customerById(@Argument Integer id) {
        return this.customerRepository.findById(id);
    }

    @SchemaMapping(typeName = "Customer")
    Flux<Order> orders(Customer customer) {
        return webClient
                .get()
                .uri("/api/v1/orders?customerId=" + customer.id())
                .retrieve()
                .bodyToFlux(Order.class);
    }

}

record Order(Integer id, Integer customerId){}

My question is how would I refactor this @SchemaMapping endpoint to use @BatchMapping and keep the app nonblocking.

I tried the following:

    @BatchMapping
    Map<Customer, Flux<Order>> orders(List<Customer> customers) {
        return customers
                .stream()
                .collect(Collectors.toMap(customer -> customer,
                        customer -> webClient
                                .get()
                                .uri("/api/v1/orders?customerId=" + customer.id())
                                .retrieve()
                                .bodyToFlux(Order.class)));
    }

But I get this error...

Can't resolve value (/customerById/orders) : type mismatch error, expected type LIST got class reactor.core.publisher.MonoFlatMapMany

... because the type of Customer has a orders LIST field and my orders service is returning a Flux.

How can I resolve this problem so I can return a Map<Customer, List<Order>> from my @BatchMapping endpoint and keep it nonblocking?

I assume it's a pretty simple solution but I don't have a lot of experience with Spring Webflux.

Thanks in advance!

0 Answers
Related