spring

Spring Framework GraphQL Tutorial

GraphQL is a relatively new concept from Facebook that is billed as an alternative to REST for Web APIs. This article will give an introduction to setting up a GraphQL server using Spring Boot so that it can be added to the existing applications or it can be used in new ones.

1. Introduction

1.1 Spring Framework

  • Spring is an open-source framework created to address the complexity of an enterprise application development
  • One of the chief advantages of the Spring framework is its layered architecture, which allows developers to be selective about which of its components they can use while providing a cohesive framework for J2EE application development
  • Spring framework provides support and integration to various technologies for e.g.:
    • Support for Transaction Management
    • Support for interaction with the different databases
    • Integration with the Object Relationship frameworks for e.g. Hibernate, iBatis etc
    • Support for Dependency Injection which means all the required dependencies will be resolved with the help of containers
    • Support for REST style web-services

1.2 GraphQL Query Language

GraphQL is a query language for APIs and a runtime for fulfilling those queries with the existing data. It provides a complete and an understandable description of the data in the API and gives clients the power to ask exactly what they need and nothing more, thus, making it easier to evolve APIs over time and enables powerful developer tools.

Traditional REST APIs work with the concept of Resources that the server manages. These resources can be manipulated in some standard ways, following the various HTTP verbs. This works very well as long as the application API fits the resource concept, but quickly falls apart when we need to deviate from it.

This also suffers when the client needs data from the multiple resources at the same time. For e.g., requesting a blog post and the comments. Typically, this is solved by either having the client make multiple requests or by having the server supply extra data that might not always be required, leading to the larger response sizes.

GraphQL offers a solution to both of these problems. It allows the client to specify exactly what data is desired, including navigation of the child resources in a single request and allows for multiple queries in a single request.

It also works in a much more RPC manner i.e. using Named Queries and the Mutations instead of a mandatory set of actions. This works to put the control where it belongs i.e. with the API developers and the API consumers. For e.g. a blog might allow the following query:

query {
    recentPosts(count: 10, offset: 0) {
        id
        title
        category
        author {
            id
            name
            thumbnail
        }
    }
}

The above query will:

  • Request the Ten most recent posts
  • For each post, requests the Id, Title, and Category
  • For each post, requests the Author returning the Id, Name, and the Thumbnail

In a traditional REST API, this either needs 11 requests i.e. 1 for the posts and 10 for the authors or developers needs to embed the author details in the post details.

1.3 GraphQL Schemas

The GraphQL server exposes a schema describing the API. This schema is made up of the type definitions where each type has one or more fields and accepts zero or more arguments thereby return a specific type. The graph is made up from the way these fields are nested with each other. Do note there is no need for the graph to be acyclic i.e. the client can get from one field to its children, but it can’t automatically get back to the parent unless the schema defines it explicitly.

type Post {
    id: Id!
    title: String!
    text: String!
    category: String
    author: Author!
}
 
type Author {
    id: Id!
    name: String!
    thumbnail: String
    posts: [Post]!
}
 
# The Root Query for the application
type Query {
    recentPosts(count: Int, offset: Int): [Post]!
}
 
# The Root Mutation for the application
type Mutation {
    writePost(title: String!, text: String!, category: String) : Post!
}

The ! at the end of the names indicates that this is a non-nullable type. Any type that does not have this can be null in the response from the server. The GraphQL service handles these correctly, allowing developers to request the child fields of the nullable types safely.

The GraphQL Service also exposes the schema itself by using a standard set of fields i.e. allowing any client to query the schema definition ahead of time. This allows the client to automatically detect when the schema changes and to allow for clients that can dynamically adapt to the way the schema works. One useful example of this is the GraphiQL Tool that allows developers to interact with any GraphQL API.

2. Introducing GraphQL Spring Boot Starter

The Spring Boot GraphQL Starter offers a fantastic way to get a GraphQL server running in a short span of time. Combined with the GraphQL Java Tools library, developers only need to write the necessary code for their service.

2.1 Setting up the service

All we need for this to work is the correct dependencies:

<dependency>
    <groupId>com.graphql-java</groupId>
    <artifactId>graphql-spring-boot-starter</artifactId>
    <version>3.6.0</version>
</dependency>
<dependency>
    <groupId>com.graphql-java</groupId>
    <artifactId>graphql-java-tools</artifactId>
    <version>3.2.0</version>
</dependency>

Spring Boot will automatically pick the above dependencies and it will set up the appropriate handlers to work automatically. By default, this will expose the GraphQL Service on the /graphql endpoint of the application and will accept POST requests containing the GraphQL Payload. This endpoint can be customized in the application.properties file if necessary.

2.2 Writing the Schema

The GraphQL Tools library works by processing GraphQL Schema files to build the correct structure and then wires special beans to this structure. Spring Boot GraphQL starter automatically finds these schema files.

These files need to be saved with the extension .graphqls and can be present anywhere in the classpath. Developers can also have as many of these files, so they can split the schema up into different modules. The one requirement is that there must be exactly one root query, and up to one root mutation. This cannot be split across files, unlike the rest of the schema. This is a limitation of the GraphQL Schema definition itself, and not of the Java implementation.

2.3 Root Query Resolver

The root query needs to have special beans defined in the Spring context to handle the various fields in this root query. Unlike the schema definition, there is no restriction that there only be a single Spring bean for the root query fields.

The only requirements are that the beans implement GraphQLQueryResolver and that every field in the root query from the schema has a method in one of these classes with the same name.

public class Query implements GraphQLQueryResolver {
    private PostDao postDao;
    public List getRecentPosts(int count, int offset) {
        return postsDao.getRecentPosts(count, offset);
    }
}

The names of the method must be one of the following, in the below order:

  • <field>
  • is<field> – Only if the field is of type Boolean
  • get<field>

The method must have parameters that correspond to any parameters in the GraphQL schema, and may optionally take a final parameter of type DataFetchingEnvironment. The method must also return the correct return type for the type in the GraphQL schema. Any simple type i.e. String, int, List etc. can be used with the equivalent Java types and the system just maps them automatically.

2.4 Using Beans to Represent Types

Every complex type in the GraphQL server is represented by a Java bean whether it is loaded from the root query or from anywhere else in the structure. The same Java class must always represent the same GraphQL type, but the name of the class is not necessary.

Fields inside the Java bean will directly map onto fields in the GraphQL response based on the name of the field i.e.:

public class Post {
    private String id;
    private String title;
    private String category;
    private String authorId;
}

Any fields or methods on the Java bean that do not map on to the GraphQL schema will be ignored, but will not cause problems. For e.g., the field id here does not correspond to anything in our schema, but it will be available to use for the next step.

2.5 Field Resolvers for Complex Values

Sometimes, the value of a field is non-trivial to load. This might involve database lookups, complex calculations, or anything else. GraphQL Tools has a concept of a field resolver that is used for this purpose. These are Spring beans that can provide values in place of the data bean.

The field resolver is any bean in the Spring Context that has the same name as the data bean, with the suffix Resolver and implements the GraphQLResolver interface. Methods on the field resolver bean follow all of the same rules as on the data bean. If a field resolver and the data bean both have methods for the same GraphQL field, then the field resolver will take precedence.

public class PostResolver implements GraphQLResolver {
    private AuthorDao authorDao;
 
    public Author getAuthor(Post post) {
        return authorDao.getAuthorById(post.getAuthorId());
    }
}

The fact that these field resolvers are loaded from the Spring context is important and this allows them to work with any other Spring managed bean. For e.g. DAOs etc.

Importantly, if the client does not request a field, then the GraphQL Server will never do the work to retrieve it. This means that if a client retrieves a Post and does not ask for the Author, then the getAuthor() method above will never be executed, and the DAO call will never be made.

2.6 Nullable Values

The GraphQL Schema has the concept that some types are nullable and others are not. This can be handled in the Java code by directly using the null values. The Optional type from Java 8 can be used here for all the nullable types and the system will do the correct thing with the values. This is very useful as it means that our Java code is obviously the same as the GraphQL schema from the method definitions.

3. Introducing GraphiQL

GraphQL also has a companion tool called GraphiQL. This is a UI that is able to communicate with any GraphQL Server and execute queries and mutations against it. It is also possible to include the web-based version of GraphiQL in our application automatically, by adding the GraphiQL Spring Boot Starter dependency.

<dependency>
    <groupId>com.graphql-java</groupId>
    <artifactId>graphiql-spring-boot-starter</artifactId>
    <version>3.6.0</version>
</dependency>

This will only work if we are hosting our GraphQL API on the default endpoint of /graphql.

That’s all for this post. Happy Learning!!

4. Conclusion

GraphQL is a very exciting new technology that can potentially revolutionize the way that Web APIs are developed. The combination of the Spring Boot GraphQL Starter and the GraphQL Java Tools libraries make it incredibly easy to add this technology to any new or existing Spring Boot applications.

Yatin

An experience full-stack engineer well versed with Core Java, Spring/Springboot, MVC, Security, AOP, Frontend (Angular & React), and cloud technologies (such as AWS, GCP, Jenkins, Docker, K8).
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button