GraphQL server using Spring Boot, Part I

Balázs Vajner
Dec 10, 2019 · 9 min read

This article focuses on how to quickly (in a matter of minutes, literally) set up a GraphQL server from scratch, utilising Spring Boot. My solution presented here is not the only possible way. There are multiple different libraries with different approaches to the same task. However, the library I used here has a mature Spring Boot support and is really easy to use. So this might be one of your fastest options.

Note: This tutorial assumes that you are familiar with the Java programming language, the Spring Boot framework, and REST APIs in general. No prior GraphQL experience is required.

This article is the first part of a series that aims to go in-depth into the topic, starting with a simple Hello World program.

GraphQL 101

GraphQL is a new specification (originally developed by Facebook for internal use, later open-sourced in 2015) that describes how to implement APIs. Unlike REST, which uses different endpoints to retrieve different types of data (e. g. users, comments, blog posts…), GraphQL exposes a single endpoint that receives a query from the front-end as part of the request, returning exactly the requested pieces of data in a single response. The server defines a schema describing what queries are available.

Two main problems with REST are over-fetching (fetching more data than needed) and under-fetching (fetching less data than needed) — a result of the relatively rigid nature of REST endpoints. The GraphQL approach of client-provided queries can mitigate both. Furthermore, by reducing the interdependence between the two sides in certain workflows, the life of back- and front-end developers becomes substantially easier. (E. g. eliminating the need for client/task-specific endpoints traditionally used to avoid the first two issues.)

Without going into the details of the theory, let’s see how it works in practice!

Preparation

The first step is to create an empty Spring Boot web project, which is most easily done by using the official Spring Initializr. For this guide, I will use Gradle, Java 11 and Spring Boot 2.2.1. You can leave the Group/Artifact name as is, or choose one of your liking. Here I will use com.example and graphql. Make sure to add Spring Web in the dependencies section.

For the GraphQL part, we will use libraries from the GraphQL Java Kickstart project. Open build.gradle and add the following dependencies:

implementation 'com.graphql-java-kickstart:graphql-spring-boot-starter:6.0.0'
runtimeOnly 'com.graphql-java-kickstart:graphiql-spring-boot-starter:6.0.0'

In the plugins section, add the Kotlin plugin:

id 'org.jetbrains.kotlin.jvm' version '1.3.60'

Note: We will not use Kotlin for this tutorial, however, some of the libraries we use are written in Kotlin and this plugin is required to compile the project.

Now that we have added all the dependencies, we need to start creating our GraphQL server. We will also have a built-in instance of GraphiQL to interact with our API. Two other clients can also be integrated automatically, GraphQL Playground and Altair. For this tutorial, the basic GraphiQL client will be sufficient.

You can, of course, use any other external GraphQL-compatible client, including Postman, which recently received GraphQL support.

Note: Unlike REST, GraphQL as a specification is not tied to the HTTP protocol, however, HTTP is most commonly used. In this case, GraphQL queries are simple HTTP GET or POST requests with special query parameters or request bodies, respectively.

Hello, GraphQL!

Time to get started for real — let’s define our first query!
To do this, first, we have to write our schema: Create a file called schema.graphqls in the resources folder, with the following content:

With that, we have defined a query called hello, which takes no arguments and returns a String. The exclamation mark indicates that the query will never return null. Query is a special type that declares the root query resolvers of the application.

Note: The file name is not important. However, the extension must be .graphqls, otherwise, the library will not find it. You do have the option to split the schema declaration into multiple files. In real-life scenarios, this is generally recommended to keep the codebase manageable. For this tutorial, you can keep everything in one file.

The approach presented here is called schema-first because we write our schema first and then the code to implement it. (The alternative approach is called code-first — when the schema is generated automatically based on the code we write.)

With our first query defined, we only need to implement the business logic behind our query. Create a package called com.example.graphql.query, and a new Java class within called HelloQueryResolver:

GraphQLQueryResolver is a marker interface that tells the underlying library to use this bean for resolving queries and will automatically match the hello method with the hello query. It is also important to make it a Spring Service (or Component).

Note: Comparing to REST APIs, resolvers are analogous to controllers. Thus, business logic should not be implemented in resolvers, but rather in the service layer.

That’s all! Let’s run our application, open http://localhost:8080/graphiql and enter the following query:

Run the query, and if everything is OK, you should see the following result:

Congratulations, you just created your first Spring Boot GraphQL Server!

A more sophisticated hello

Now let’s make things a bit more interesting. Let the user specify whom to say hello to! Modify the query as follows:

Here we added a parameter named Who, with the type of String. Notice that there is no ! after the type name, indicating that this parameter is optional.

Note: GraphQL supports the following built-in types: String, Int, Float,Boolean and ID. These are called scalars in the GraphQL nomenclature because they do not have fields themselves. Later on, we will see how to define our own types (complex objects) and scalars.

Now modify the resolver accordingly:

Note: Alternatively, you could you use Optional directly in the method signature:

public String hello(final Optional<String> who) {
return String.format("Hello, %s!", who.orElse("GraphQL"));
}

However, using Optional in method signatures is a controversial topic, and is generally not considered a best practice.

Now if you run our previous query, it will still work. Since the only parameter of our query is optional, we can omit it. However, now we can do the following:

Which will yield the following result:

Note: Comparing to REST APIs, queries are analogous to GET requests. Thus, queries should not do modifications.

Mutations

Now let’s go forward with the equivalents of POST requests — mutation. Mutations are defined just like queries:

Similarly to Query, Mutation is a special type that declares our mutations. This mutation takes two arguments, firstName and lastName. Both arguments are required, as indicated by the !. The mutation returns a Person object. And where is this Person type coming from? We have to define it, of course:

Notice that an id field is added here, to uniquely identify person instances. It will be generated automatically by our service.

Now implement the Java part of the schema! To keep our project organised, create a package named com.example.graphql.model and the POJO class representing the Person type:

To handle the business logic of creating and retrieving people, we will create a service. (Remember, our resolvers are the controllers, which should not contain business logic!)

This service simply stores objects in an in-memory collection. In a real-life application, it would use some database to do that, but here we are concentrating on GraphQL.

With our POJO and service in place, the only thing to do is to implement the mutation resolver. First, create a package for our mutations, com.example.graphql.mutation and then the mutation resolver:

As you can see, this is also very similar to our query resolver. Notice that a different marker interface is implemented, the GraphQLMutationResolver interface.

Run/restart the app, and our new mutation should be available:

Which should produce the following result:

Note: There is no void in GraphQL. Also, if the return type is not a scalar but a complex type as in this case, at least one field must be selected.

Querying objects

Now that we can create people, it is time to query them. Declare a new query in the Query type:

This declares that our new query takes no arguments (we simply return all people), returns an array (indicated by the []) of Person objects. Notice the two ! — the one inside the brackets indicates that there will be no null elements in the array, the one outside indicates that the array itself may not be null. (An empty array will be returned if there are no people created yet.)

Note: In many cases, this is a best practice for arrays — and it makes the front-end developer’s life much easier.

The resolver part is very simple. We will use PersonService to retrieve the already created people:

After restarting the application, we can use the following query:

Note that even if this returns an array of Person objects, we select the fields in the same way as we selected when the mutation returned a single Person object. However, in the response, we will receive an array of Person objects:

Computed fields

In most cases, not all of the data is directly stored in the database. For example, we may want to have a fullName field in our Person type:

Since the Person class does not have such a field, we must provide a resolver for the Person type that returns this information. Create a package called com.example.graphql.resolver, and a new Java class within in it called PersonResolver:

This is very similar to how we defined query and mutation resolvers. Note that the class implements the GraphQLResolver interface, which takes the target class / GraphQL type as a type parameter. We have to define a resolver method for each GraphQL field that is not matched with the fields of the corresponding Java class.

With the updated type, the query result will look like as follows:

Note: If we define a resolver method with the same name as a field or a getter method in the POJO class, the resolver will take precedence.

There is one very important thing to keep in mind at this step: a resolver method will only be called if we actually query that particular field. Otherwise, it will never be called. This is one of the main benefits of GraphQL over REST.

Next steps

So far we went through the very basic building blocks of a GraphQL API. There are many more topics to cover: nested objects (and the tools available to optimise queries with deeply nested objects), the intricacies of the GraphQL type system (input objects, interfaces, unions, custom scalars), error handling, unit testing, security, subscriptions (the GraphQL way using a web socket), the data fetching environment and much more.

In the following parts of this series, we’ll explore these topics and how to implement them.

Finally, you can find the source code for the entire project at https://github.com/team-supercharge/spring-boot-graphql-tutorial


At Supercharge, we are a next-generation innovation partner working with our clients to create transformative digital solutions. If you liked this article, check out some of Supercharge’s other articles on our blog, and follow us on LinkedIn, and Facebook. If you’re interested in open positions, follow this link.

Supercharge's Digital Product Guide

As innovation partners, Supercharge works with clients to create transformative digital solutions. We create digital strategies, design delightful interfaces and build robust software. We are happy to share with you what we learn on this journey.

Balázs Vajner

Written by

Supercharge's Digital Product Guide

As innovation partners, Supercharge works with clients to create transformative digital solutions. We create digital strategies, design delightful interfaces and build robust software. We are happy to share with you what we learn on this journey.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade