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!