Unified GraphQL API for gRPC microservices using Rejoiner and Spring Boot
Rejoiner is a fairly young framework with the goal of bringing together two powerful and increasingly popular technologies, GraphQL and gRPC.
It creates a unified GraphQL schema for gRPC microservices and provides DSL to modify it.
In this tutorial, we are going to create several gRPC microservices and see how we can use Rejoiner to expose them via a single GraphQL API.
1. gRPC microservices
We will start by creating gRPC services.
If you are new to using the gRPC, check out the quick start guide or the more detailed gRPC basics.
1.1. Author Service
We define our first service in a proto file:
Then we implement the service by extending the class generated from the proto file:
Finally, we create a class that starts the server serving our service:
1.2. Book Service
We do the same for the second service. Create a proto file:
Implement the service:
Create a class starting the server:
Now we move on to creating a gateway that will expose our services via GraphQL.
We will use Google Guice to initialize all the components used by Rejoiner,
as it utilizes Guice internally and requires this.
2.1.1 gRPC Clients
First, we create modules in which we define Guice bindings for stubs used to communicate with gRPC services:
And for convenience, we group these modules into one:
SchemaModule is a core component of Rejoiner.
It looks at the parameters and return type of methods that have Rejoiner annotations to generate parts of the schema,
which are then combined into the final schema using SchemaProviderModule.
We create a schema module for the first service using Rejoiner’s @Query and @Mutation annotations:
We also create a schema module for the second service using the same annotations,
but note that the mutation here actually updates data in both services:
And since SchemaModule is a Guice module, we can combine these modules into one module as well:
2.2. Spring Boot
Rejoiner provides us with a GraphQL schema, but we need to bind it to an HTTP server.
We will use spring-boot-starter-web to built a web application and graphql-spring-boot-starter to turn it into a GraphQL server.
Using Spring Boot not only simplifies the creation of our application
but also makes it easy to add features such as authentication to our gateway, if necessary.
First, we create a standard main class to bootstrap our Spring application:
We need to add an initializer block to initialize the Guice modules before the Spring context:
Then we create a bean of the GraphQLSchemaProvider type which uses the Rejoiner-generated schema obtained from Guice Injector:
Finally, we need to register a custom Instrumentation to support Guava’s ListenableFuture:
Our gateway is ready, and we can run it for testing, but first, we will look at some additional features of Rejoiner.
2.3. Data loader
Now we will see how we can use the other annotations provided by Rejoiner to modify the schema, and add Data Loader support for batch loading.
We will use the book service stub to implement batch loading of books. We get it from the injector and declare a bean:
We need to implement BatchLoader<K, V> functional interface whose first type indicates the type of keys used for data loading requests,
and the second type indicates the type of returned values.
In our case, we will load books by their identifier, so we need to implement BatchLoader<Integer, Book>.
We also need to use FutureConverter to turn ListenableFuture into CompletableFuture required by the interface:
To start using our BatchLoader, we have to register it in DataLoaderRegistry, which in turn needs to be added to GraphQLContext:
To use the data loader in our schema modules, we need to obtain it from DataFetchingEnvironment.
Let’s try to use schema modifications to remove the field with book IDs from the author schema
and add a field with book objects loaded using the data loader:
Now we can run our application for testing (do not forget to also start the gRPC services).
We can use any third-party GraphQL client or add graphiql-spring-boot-starter as a dependency to embed GraphiQL tool
which becomes accessible at the root /graphiql
First we create an author:
Then we create a book of this author using the author ID received in the first request:
And create another book using the same author ID:
Finally, we query the author along with the titles of the nested books by his ID:
GRPC and GraphQL are great technologies that complement each other perfectly when creating an application with a microservice architecture.
GraphQL gives clients the ability to request exactly the data they need, while gRPC makes internal communication between services more efficient,
and Rejoiner makes it easier to use them together.