Getting Started with GraphQL and Spring Boot

GraphQL Spring Boot Tutorial

1. Overview

Let’s get started with GraphQL in Spring Boot application. In this app we will try mimicking simple Shopping cart application with Customers, Orders and some Products. The app would give us an overview of how to add GraphQL support to Spring Boot using GraphQL Java Tools and GraphQL Spring Boot starter.

Before we deep dive into code, let’s have an overview of GraphQL.

2. Introduction to GraphQL

Facebook opened source GraphQL in 2015 and since then it has been on a roll. There is tremendous amount of adoption from Tech companies such as Github, Shopify, Airbnb etc. Since GraphQL is just a specification, open source community has been busy writing tools and support for GraphQL in all possible known languages.

GraphQL provides clients to specify the fields they want in the API request and server response will only contain those fields. This solves the problem of underfetching or overfetching that REST has. Clients are in power here and based on the user experience they can fetch as much or as little information they want.

GraphQL is schema driven, meaning we need to first design the schema for our API. The schema act as both contract between the client and server and also documentation for our business domain. I think this design first principle gives GraphQL an edge over REST. Swagger, RAML, JSONSchema tries to elevate some of these pain points, however since they are not mandatory most of the API design lack some sort of documentation.

2.1 Overview of GraphQL Schema

Schema design is the first thing we would look at. GraphQL schema are typed meaning we need to specify the types and attributes of each type.

GraphQL typeDescription
QueryIs used for fetching information. Similar to GET method.
MutationIs used for inserting, updating or deleting information. Similar to POST, PUT or PATCH methods in REST.
SubscriptionIs used for streaming the information from server similar to Websocket

For this GraphQL Spring Boot tutorial, subscription will be out of scope. We will tackle that beast in another tutorial :-)

Let’s create the graphql schema. There are 3 graphql types for this app: Customer, Product and Order. Each type has a few attributes:

GraphQL Spring Boot Schema Design

We will shortly see how to write GraphQL schema in SDL (schema definition language).

3. GraphQL support in Spring Boot

The next step would be to create Spring Boot app with GraphQL support. Let us scaffold the app using start.spring.io. We will be using following settings for Spring Initilizer.

  1. Gradle project
  2. Java 11
  3. Spring Boot 2.1.6
  4. Group: net.viralpatel Artifact: spring-boot-graphql-tutorial.
  5. Dependencies: Spring Data JPA

Download the generated source code and open it in Intellij or Eclipse.

3.1 GraphQL dependencies

Let us add GraphQL Java and other dependencies in Gradle. Open build.gradle file and add following code:

build.gradle

plugins { id 'org.springframework.boot' version '2.1.6.RELEASE' id 'java' } apply plugin: 'io.spring.dependency-management' group = 'net.viralpatel' version = '0.0.1-SNAPSHOT' sourceCompatibility = '11' repositories { mavenCentral() } dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'com.h2database:h2' implementation 'com.graphql-java-kickstart:graphql-spring-boot-starter:5.9.0' implementation 'com.graphql-java-kickstart:graphql-java-tools:5.6.0' implementation 'com.graphql-java-kickstart:graphiql-spring-boot-starter:5.9.0' testImplementation 'org.springframework.boot:spring-boot-starter-test' }
Code language: JavaScript (javascript)

We are adding following dependencies:

  1. spring-data-jpa and h2 for creating data repositories to store customers, products and orders.
  2. graphql-spring-boot-starter for adding GraphQL Java support with Spring Boot.
  3. graphql-java-tools is schema first tool inspired from GraphQL Tool for JS which let us design the schema first and generate all boilerplate graphql java configuration.
  4. graphiql-spring-boot-starter adds support for GraphiQL (notice the extra ‘i’). GraphiQL provides nice editor to query and introspect GraphQL API.

Since we are using GraphQL Java Spring Boot, we need to fix the kotlin version to 1.3.10.

Create gradle.properties file and add following code in it.

gradle.properties

kotlin.version = 1.3.10

3.2 GraphQL Schema SDL

The GraphQL schema can be written using GraphQL SDL (Schema Definition Language). Following is our app’s GraphQL schema.

Schema definition file is under src/resources directory. GraphQL Spring Boot starter will read this and configure it using graphql-java-tools.

src/resources/schema.graphqls

type Query { customerById(id: ID!): Customer } type Customer { id: ID! name: String! email: String! orders: [Order] } type Order { id: ID! customer: Customer! product: Product! quantity: Int! status: String! } type Product { id: ID! name: String description: String price: String }
Code language: JSON / JSON with Comments (json)

3.3 Java POJOs for GraphQL

Next we create POJOs for each graphql type: Customer, Product and Order.

Customer.java

package net.viralpatel.springbootgraphqljava.customers; import net.viralpatel.springbootgraphqljava.orders.Order; import java.util.List; public class Customer { private Long id; private String name; private String email; private List<Order> orders; // getters and setters }
Code language: Java (java)

Order.java

package net.viralpatel.springbootgraphqljava.orders; import net.viralpatel.springbootgraphqljava.customers.Customer; import net.viralpatel.springbootgraphqljava.products.Product; import java.time.LocalDate; public class Order { private Long id; private Customer customer; private Product product; private Integer quantity; private String status; private LocalDate created; // getters and setters }
Code language: Java (java)

Product.java

package net.viralpatel.springbootgraphqljava.products; public class Product { private Long id; private String name; private String description; private Double price; // getters and setters }
Code language: Java (java)

3.4 GraphQL Query Resolvers

Once we defined the POJOs for our GraphQL schema we need to define the Resolvers. Resolvers are core to GraphQL. Each resolver is responsible for fetching/retrieving data for any given type and its attribute. Let us start with the root resolver. Remember Query is the root which exposes customerById.

QueryResolver.java

package net.viralpatel.springbootgraphqljava; import com.coxautodev.graphql.tools.GraphQLQueryResolver; @Component public class QueryResolver implements GraphQLQueryResolver { private CustomerRepository customerRepository; public QueryResolver(CustomerRepository customerRepository) { this.customerRepository = customerRepository; } public Customer customerById(Long id) { return customerRepository .findById(id) .orElse(null); } }
Code language: Java (java)

Notice how we implemented GraphQLQueryResolver interface. This would let GraphQL Java knows we are intending to use this as root resolver for Query.

QueryResolver exposes a public method customerById which takes Id as input and returns the customer object. Notice we using Spring Data JPA to load the customer record from our database. I have skipped some of the code related to mapping Spring Data model to GraphQL Pojo for simplicity. You can check the full source code in Github repository.

Next we create custom resolvers for each type.

CustomerResolver.java

package net.viralpatel.springbootgraphqljava.customers; import com.coxautodev.graphql.tools.GraphQLResolver; @Component public class CustomerResolver implements GraphQLResolver<Customer> { private OrderRepository orderRepository; @Autowired public CustomerResolver(OrderRepository orderRepository) { this.orderRepository = orderRepository; } public List<Order> orders(Customer customer) { return orderRepository.findByCustomerId(customer.getId()) .stream() .collect(Collectors.toList()); } }
Code language: Java (java)

Notice how we implemented GraphQLResolver<T> interface for Customer class. GraphQL Java tools would identify this as resolver for Customer class. Here we implement a method orders() that GraphQL would call when it wants to resolve all orders for given customer.

Also notice these resolvers are annotated as Spring @Components. Hence we can inject any Spring bean in the resolvers.

OrderResolver.java

package net.viralpatel.springbootgraphqljava.orders; import com.coxautodev.graphql.tools.GraphQLResolver; @Component public class OrderResolver implements GraphQLResolver<Order> { private ProductRepository productRepository; private CustomerRepository customerRepository; public OrderResolver(ProductRepository productRepository, CustomerRepository customerRepository) { this.productRepository = productRepository; this.customerRepository = customerRepository; } public Customer customer(Order order) { return customerRepository .findById(order.getCustomer().getId()) .orElse(null); } public Product product(Order order) { return productRepository .findById(order.getProduct().getId()) .orElse(null); } }
Code language: Java (java)

Last but not least, the OrderResolver will resolve the attributes for Order type. Each order has customer and product attribute which resolve by querying appropriate Spring Data repositories.

3.5 GraphQL Mutation Resolvers

Next up is the mutations. Mutation are nothing but a way of altering the data in GraphQL API. Like Query, Mutation is also a top-level type.

Let’s define following mutation type in schema.graphqls

type Mutation { createOrder(order: CreateOrderInput!): Order! } input CreateOrderInput { customerId: ID! productId: ID! quantity: Int! }
Code language: JSON / JSON with Comments (json)

We have added method createOrder within Mutation type. Also note that the order parameter is of type CreateOrderInput. Input type is another type used specifically in mutation.

Input types can’t have fields that are other objects, only basic scalar types, list types, and other input types.

Once the mutation schema is defined, we can declare class OrderMutationResolver implementing GraphQLMutationResolver interface to handle createOrder mutation.

OrderMutationResolver.java

package net.viralpatel.springbootgraphqljava.orders; import com.coxautodev.graphql.tools.GraphQLMutationResolver; @Component public class OrderMutationResolver implements GraphQLMutationResolver { private OrderRepository orderRepository; public OrderMutationResolver(OrderRepository orderRepository) { this.orderRepository = orderRepository; } public Order createOrder(CreateOrderInput createOrderInput) { Order order = new Order(); order.setCustomerId(createOrderInput.getCustomerId()); order.setProductId(createOrderInput.getProductId()); order.setQuantity(createOrderInput.getQuantity()); order.setStatus("PENDING"); orderRepository.save(order); return order; } }
Code language: Java (java)

The createOrder method creates a new order using OrderRepository and returns the object.

4. Build and run

Start the Spring Boot application by running SpringBootGraphqlJavaApplication class or by running gradle:

./gradlew bootRun
Code language: Bash (bash)

4.1 Introspecting using GraphiQL

Once the Spring Boot app is started on default port 8080, open http://localhost:8080/graphiql

Try running following GraphQL query and see the output.

query { customerById(id: 1) { name orders { id status product { name } } } }
Code language: JSON / JSON with Comments (json)

Also run following mutation query to create a new order.

mutation { createOrder(order: { customerId: 1 productId: 1 quantity:4 }) { id status } }
Code language: JSON / JSON with Comments (json)

5. Download

The project is available on Github.

Github – Spring Boot GraphQL Java

Get our Articles via Email. Enter your email address.

You may also like...

2 Comments

  1. Tipi says:

    The error has been occurred when we pass a Unicode text to query.
    Is there any solution?

    • Oh, I haven’t tried that yet. I’ll update if I have any solution.

Leave a Reply

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