Auto Added by WPeMatico

How to build a GraphQL Gateway with Spring Boot and Kotlin

With Spring Boot + Kotlin + Coroutines + GraphQL-java-kickstart, you can build a GraphQL Gateway with a minimum of boilerplate.

Up and Running

The code is available here:

jmfayard/spring-playground

Run the server like this:

git clone https://github.com/jmfayard/spring-playground
cd spring-playground/graphql-gateway
./gradlew bootRun

Open GraphiQL at http://localhost:8080/

Animal facts

With this project up and running, you can fetch animal facts using a GraphQL Query.

Enter this query:

query {
dog {
fact
length
latency
}
cat {
fact
length
latency
}
}

Run the query, and you will see something like this:

If you are new to the GraphQL, read this introduction from @methodcoder, I will wait.

https://medium.com/media/63ea00cc1e27ec6be082d27c75f797cc/href

Cat facts and dog facts

Where do the animal facts come from?

The server knows about two REST APIs.

The first one is about cat facts:

$ http get https://catfact.ninja/fact
{
"fact": "Isaac Newton invented the cat flap. Newton was experimenting in a pitch-black room. Spithead, one of his cats, kept opening the door and wrecking his experiment. The cat flap kept both Newton and Spithead happy.",
"length": 211
}

And the second one is about dog facts:

$ http get https://some-random-api.ml/facts/dog
{
"fact": "A large breed dog's resting heart beats between 60 and 100 times per minute, and a small dog breed's heart beats between 100-140. Comparatively, a resting human heart beats 60-100 times per minute."
}

By building a simple gateway, we take on complexity so that the front-end developers have one less thing to worry about:

  • We take care of calling the multiple endpoints and combining them, becoming a backend-for-frontend.
  • We offer a nice GraphQL schema to the front-end(s).
  • We normalize the response format — dog facts have no length attribute, but we can compute it!
  • We can potentially reduce the total response time. Without the gateway, the front-end would do two round-trips of let say 300 ms, so 600ms. With the gateway, there is one round-trip of 300 ms and two round-trips between the gateway and the facts server. If those are located on the same network, those could be done in 10 ms each, for a total of 320 ms.

So, how do we build that gateway?

Dependencies

If you start a new project from scratch via https://start.spring.io/, you will need to add those dependencies:

  • Spring Webflux
  • GraphQL-java
  • GraphQL-java-kickstart libraries

Use the source Luke

Note that I’m using gradle refreshVersions to make it easy to keep the project up-to-date. Therefore, the versions are not defined in the build.gradle files, they are centralized in the versions.properties file. RefreshVersions is bootstrapped like this in settings.gradle.kts:

https://medium.com/media/dbf2e6d4c43feb33b55a03a17f3d2745/href

GraphQL-schema first

GraphQL-java-kickstart uses a schema-first approach.

We first define our schema in resources/graphql/schema.grqphqls :

https://medium.com/media/1291e72dbc48136069a2020704b51c0d/href

Then, we tell Spring where our GraphQLSchema comes from:

https://medium.com/media/b38095cf5b3757ebd8e33dc5602da64a/href

Spring wants at least a GraphQLQueryResolver, the class responsible for implementing GraphQL queries.

We will define one, but keep it empty for now:

@Component
class AnimalsQueryResolver() : GraphQLQueryResolver {

}

GraphQLQueryResolver

If we start our application with ./gradlew bootRun, we will see it fail fast with this error message:

FieldResolverError: No method or field found as defined in schema graphql/schema.graphqls:2 
with any of the following signatures
(with or without one of [interface graphql.schema.DataFetchingEnvironment] as the last argument),
in priority order:
  dev.jmfayard.factsdemo.AnimalsQueryResolver.cat()
dev.jmfayard.factsdemo.AnimalsQueryResolver.getCat()
dev.jmfayard.factsdemo.AnimalsQueryResolver.cat

The schema, which is the single source of truth, requires something to implement a cat query, but we didn’t have that in the code.

To make Spring happy, we make sure our Query Resolver has the same shape as the GraphQL schema:

https://medium.com/media/0d2edf3a38f5a0fd7dcea24a01eba212/href

Notice that you can directly define a suspending function, without any additional boilerplate, to implement the query.

Run again ./gradlew bootRun and now Spring starts!

We go one step further by forwarding the calls to an AnimalsRepository:

https://medium.com/media/9ee243fede53ce85853e83574d996465/href

How do we implement this repository? We need an HTTP client.

Suspending HTTP calls with ktor-client

We could have used the built-in reactive WebClient that Spring provides, but I wanted to use ktor-client to keep things as simple as possible.

First, we have to add the dependencies for ktor, http and kotlinx-serialization, then configure our client.

See the commit Configure ktor-client, okhttp & kotlinx.serialization

The most interesting part is here:

https://medium.com/media/e7cdcf169aae2a925e61c6317d10900e/href

Simple or non-blocking: why not both?

When I see the code above, I am reminded that I love coroutines.

We get to write code in a simple, direct style like in the old days when we were writing blocking code in a one-thread-per-request model.

Here it’s essential to write non-blocking code: the gateway spends most of its time waiting for the two other servers to answer.

Code written using some kind of promise or reactive streams is therefore clearly more efficient than blocking code.

But those require you to “think in reactive streams” and make your code looks different indeed.

With coroutines, we get the efficiency and our code is as simple as it gets.

Resilience via a Circuit Breaker

We have a gateway, but it’s a bad gateway.

More precisely, it’s as bad as the worst of the servers it depends on to do its job.

If one server throws an error systematically or gets v e r y s l o w, our gateway follows blindly.

We don’t want the same error to reoccur constantly, and we want to handle the error quickly without waiting for the TCP timeout.

We can make our gateway more resilient by using a circuit breaker.

Resilience4j provides such a circuit breaker implementation.

We first add and configure the library.

See the commit: add a circuit breaker powered by resilience4j.

The usage is as simple as it gets:

https://medium.com/media/a85ff23fcfeea577b3a619daf5817efa/href

I want to learn more

See spring-playground/graphql-gateway

jmfayard/spring-playground

The talk that inspired this article: KotlinFest2019「Future of Jira Software powered by Kotlin」 #kotlinfest — YouTube

https://medium.com/media/79fe002f558d781672934e302da05b1a/href

Documentation of the libraries used in this project:

Another approach: Creating a Reactive GraphQL Server with Spring Boot and Kotlin

If you want to contact me, there is a standing invitation at https://jmfayard.dev/contact/.

Click 👏 to say “thanks!” and help others find this article.

To be up-to-date with great news on Kt. Academy, subscribe to the newsletter, observe Twitter and follow us on Medium.

If you need a Kotlin workshop, check how we can help you: kt.academy.


How to build a GraphQL Gateway with Spring Boot and Kotlin was originally published in Kt. Academy on Medium, where people are continuing the conversation by highlighting and responding to this story.

Continue ReadingHow to build a GraphQL Gateway with Spring Boot and Kotlin

End of content

No more pages to load