Getting Started with GraphQL and Spring Boot

Getting Started with GraphQL and Spring Boot

GraphQL is a relatively new concept from Facebook. GraphQL is a query language to retrieve data from a server, as an alternative to REST, SOAP, or gRPC.

We’ll learn how to set up a GraphQL server using Spring Boot so that we can add it to existing applications or use it in new ones.

1. What Is GraphQL ?

Traditional REST APIs are built around managing server-managed resources, manipulated through standard HTTP verbs. While effective within this framework, they struggle when deviating from it or when clients require data from multiple resources simultaneously, often resulting in larger response sizes due to unnecessary data

GraphQL addresses these challenges by empowering clients to request precisely the data they need in a single query, facilitating navigation of child resources, and enabling multiple queries at once.

For example, a blog might allow the following query:

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

This query will:

  • request the ten most recent posts
  • for each post, request the ID, title, and category
  • for each post, request the author, returning the ID, name, and thumbnail

In a traditional REST API, this either needs 11 requests, one for the posts and 10 for the authors, or needs to include the author details in the post detail

2. GraphQL Schemas

The GraphQL server exposes a schema describing the API. This schema consists of type definitions. Each type has one or more fields, each taking zero or more arguments and returning a specific type.

An example GraphQL Schema for a blog may contain the following definitions describing a Post, the Author of the post, and a root query to get the most recent posts on the blog:

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 {
    createPost(title: String!, text: String!, category: String, authorId: String!) : Post!
}

The “!” at the end of some names indicates that it’s a non-nullable type.
The GraphQL Service also exposes the schema using a standard set of fields, allowing any client to query for the schema definition ahead of time.

3. Introducing GraphQL Spring Boot Starter

The Spring Boot GraphQL Starter offers a fantastic way to get a GraphQL server running in a very short time. Using autoconfiguration and an annotation-based programming approach, we need only write the code necessary for our service.

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

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-graphql</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

Because GraphQL is transport-agnostic, we’ve included the web starter in our config. This exposes the GraphQL API over HTTP using Spring MVC on the default /graphql endpoint.

4. Writing the Schema

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

We need to save these “.graphqls” or “.gqls” schema files under src/main/resources/graphql/** location, and Spring Boot will pick them up automatically.

5. Implementing Our Schema

Once we’ve written our schema, we need to be able to implement it in code. This consists of three different types of code that we can implement:

  • Root Query Resolver – These are used to resolve values of top-level queries from our schema.
  • Field Resolver – These are used to resolve values nested within our response.
  • Mutations – These are used to implement mutations from our schema.

All of these are implemented by writing beans within our application and annotating them as appropriate.

5.1 Root Query Resolver

We need to annotate the handler methods with @QueryMapping annotation and place these inside standard @Controller components in our application. Unlike the schema definition, there’s no restriction that there only be a single Spring bean for the root query fields.

@Controller
public class PostController {

    private PostDao postDao;

    @QueryMapping
    public List<Post> recentPosts(@Argument int count, @Argument int offset) {
        return postDao.getRecentPosts(count, offset);
    }
}

The above defines the method recentPosts, which we’ll use to handle any GraphQL queries for the recentPosts field in the schema defined earlier.

5.2 Using Beans to Represent Types

Every complex type in the GraphQL server is represented by a Java bean, Fields inside the Java bean will directly map onto fields in the GraphQL response based on the name of the field

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

Any fields or methods on the Java bean that don’t map onto the GraphQL schema will be ignored but won’t cause problems. This is important for field resolvers to work.

5.3 Field Resolvers for Complex Values

The @SchemaMapping annotation maps the handler method to a field with the same name in the schema and uses it as the DataFetcher for that field.

@SchemaMapping
public Author author(Post post) {
    return authorDao.getAuthor(post.getAuthorId());
}

Importantly, if the client doesn’t request a field, then the GraphQL Server won’t do the work to retrieve it. Alternatively, we can also specify the parent type name, and the field name in the annotation

@SchemaMapping(typeName="Post", field="author")
public Author getAuthor(Post post) {
    return authorDao.getAuthor(post.getAuthorId());
}

5.4 Nullable Values

The GraphQL Schema has the concept that some types are nullable and others aren’t. We can use the new Optional type from Java 8 directly for nullable types, and the system will do the correct thing with the values.

5.5 Mutations

GraphQL also has the ability to update the data stored on the server through mutations.

Mutations should be used to inform the client that this will cause a change to the data being stored. Similar to Query, mutations are defined in the controller by annotating the handler method with @MutationMapping

@MutationMapping
public Post createPost(@Argument String title, @Argument String text,
  @Argument String category, @Argument String authorId) {

    Post post = new Post();
    post.setId(UUID.randomUUID().toString());
    post.setTitle(title);
    post.setText(text);
    post.setCategory(category);
    post.setAuthorId(authorId);

    postDao.savePost(post);

    return post;
}

6. GraphiQL

Postman can make requests using GraphQL, you can find more how to use at HERE

Also, Spring GraphQL comes with GraphiQL built-in. This is turned off by default, but we can turn it on by adding the following to application.yml

spring:
  graphql:
    graphiql:
      enabled: true

7. Summary

GraphQL is an exciting new technology that can potentially revolutionize how we develop Web APIs.

Spring Boot GraphQL Starter makes it easy to add this technology to any new or existing Spring Boot application.

As always, code sample can be found on GitHub.

In the next article, we will learn more about Advanced GraphQL for generating dynamic queries with filters

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

Your email address will not be published. Required fields are marked *