Distributed application and GraphQL

How to code a distributed application with multiple microservices and connect them all with GraphQL.

Alberto Rossotto
Geek Culture
9 min readJul 4, 2021

--

Photo by 贝莉儿 DANIST on Unsplash

Studying the implementation of GraphQL, I realized that it’s possible to use that technology to spread the business model across multiple microservices and let the API do the heavy lifting for the integration.

I built a simple demo application to put the theory into practice. The use case is, of course, pretty straightforward: there are countries and countries contains cities. As a user, I want to search countries, cities, cities from a country, and a country from a city.

In a SQL database my model would be two tables with a foreign key in City pointing to the primary id of Country.

The next step is to define my services. I opted for three nodes: City Service, Country Service, and a Frontend Node to integrate them all. City Service and Country Service never interact with each other, and Frontend Node is the only one aware of the complete model.

User doesn’t know anything about the complexity behind the frontend node

All the code is available in https://github.com/alros/distributed-graphql, and here I’ll focus on only a few parts.

First data service

I’ll start from Country Service. The first step is to create a basic Spring Boot application with these two modules:

spring-boot-starter-web is essential to expose the API via HTTP. The next step is to add the dependencies for GraphQL:

Please note that my strategy for the API definition is contract-first because I find it easier. It’s always possible to work code-first.

By default, the application will look for descriptors in resources/graphql. I created schema.graphqls with the following definition:

If you are unfamiliar with the language, this defines the type City for the data and the special type Query that contains the accepted queries.

  • allCities returns an array of cities (City is in [..])
  • city accepts a parameter id (mandatory because there’s a “!”) and returns a City
  • citiesInCountry accepts a parameter countryId and returns an array of cities.

The code part is heavily supported by coding conventions to automatically wire the schema to the code. The framework will look for a pojo called “City” to map the type and a class Query implementing GraphQLQueryResolver to map all the queries.

All methods are the translation in Java of the defined queries. This Query component is the entry point of the City Service, and, from here, it’s possible to implement whatever logic is needed to expose the data. In my example, I simply hardcoded some stuff in the repositories.

And… that’s it! It’s now possible to start the application and interact with the service via http://localhost:8080/graphiql

This is the console exposed automatically by the application

Using GraphQL, the user describes the expected model. For example:

will return

while

will return

It’s all handled by the framework and it will be important later.

Second data service

Next step is to code Country Service. I’ll skip the description here since it’s just like City Service with a different model. A note. By default Spring Boot starts on 8080, so I configured the two data services on 8090 and 8091. This may not be important working with containers, for example.

Frontend node — API

When the data services are up and running, it comes the time of the Frontend node. This node presents the additional challenge of interacting with the GraphQL API exposed by the data services.

In the pom of Frontend, Spring Boot and GraphQL are just like the data services:

Next step is to define the schema of the API exposed to the user.

The definition is a sort of merge of the two data services, but there are two huge differences: in City, country is of type Country, and in Country cities is an array of type City.

The user will be able to perform a query like the following:

It will return a richer data model that mixes the two data models.

Let’s explore the mapping.

Just like in City Service, there will be the classes City and Country to model the types, but the Java implementation will still use the primary-key-foreign-key approach:

and

There’s the usual class Query to map the queries:

Up to here, it’s the same as the data services, and for a simple query such as the following, it may even be enough.

However, if the user queries all the cities in a country, GraphQL needs a way to resolve the relation. The new component to be introduced is a GraphQLResolver.

The resolver maps the relation with a method called “cities” that accepts a Country. The method is called automatically by the framework when the user requests the field “cities”.

The mechanism works in the same way when the user requests the country of a given city:

GraphQL will search for a GraphQLResolver returing a Country given a City:

At this point the interaction user / Frontend is ready. What’s missing is the integration Frontend / data services.

Frontend node — Integration with data services

There are a few different options to call a GraphQL server from a Java application, but all have some disadvantages. I opted for Apollo Android that, despite the name, is not specific for Android. It seems one of the most complete and supported with the advantage of offering type-safe API, but it’s not exactly the easiest framework to use.

The dependency is:

Apollo also requires a plugin:

What is this plugin? As those familiar with SOAP Webservices may guess, Apollo parses the graphql schema of the services to generate code and expose type-safe API to remote services. I’m not a big fan of this, but it works pretty well once the setup is in place.

The plugin in the pom.xml will look for descriptors of the queries that the project must integrate. Descriptors must be located in src/main/graphql/serviceName, so, in this case, in src/main/graphql/cityService and src/main/graphql/countryService.

CityService.graphql will look like this:

CityFragment is a sort of reusable type. Defining fragments is useful because it reduces duplication in the generated code. Each query will be translated in a class, and the parameters will become the signatures of the constructors.

In Frontend, Query uses CityService and CountryService to retrieve the data and these two classes have the responsibility of interacting with the data services. Let’s explore CityService.

First of all, the class needs an Apollo Client:

Then there are the methods to retrieve the data, and this is where it gets convoluted, unfortunately.

The “new GetCityQuery(id)” in the last line comes from the generated code. The rest is mostly my code wrapping Apollo’s code to perform the query and parse the response in the callback. Apollo works with callbacks and RemoteServerCallback is my extension of ApolloCall.Callback.

It’s not necessary to follow my implementation on this aspect, but I find this solution easier to avoid juggling API calls with asynchronous callbacks.

The missing part is the method “performQuery” that I’ve hidden under the carpet given the amount of Generics that makes it unreadable.

Test

Finally, it’s time to test the Frontend node and see what happens.

It returns the following:

This will result in this logging:

Frontend calls Country Service once. No involvment of City Service.

Another case. Like above, but I also want the cities:

It returns the following:

This will result in this logging:

Frontend calls Country Service to retrieve all countries, then, for each Country, it calls City Service to retrieve all the cities in the given country

Final case. Given a City’s id, I want the City’s name and the name of its country:

This returns:

With logging:

Frontend calls City Service to retrieve the City with id 1, then, via CountryResolver, it calls Country Service to translate the City’s countryId into a Country.

Final considerations

GraphQL and this design look like a viable solution to integrate a complex model while keeping the single services separate and focused.

GraphQL drastically reduces the number of calls from the user because a single query can return the equivalent of many REST calls. However, multiple queries are just hidden inside the system. It appears clear from this example that the number of internal calls can explode in the absence of controls to prevent it.

The GraphQL framework to expose API is easy to use and requires minimal work. On the other end, Apollo is a reminiscence of an almost forgotten past writing ant-tasks to generate code from WSDL and integrate poorly named classes into the application.

Links

--

--