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 type | Description |
---|---|
Query | Is used for fetching information. Similar to GET method. |
Mutation | Is used for inserting, updating or deleting information. Similar to POST , PUT or PATCH methods in REST. |
Subscription | Is 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:
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.
- Gradle project
- Java 11
- Spring Boot 2.1.6
- Group:
net.viralpatel
Artifact:spring-boot-graphql-tutorial
. - 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:
spring-data-jpa
andh2
for creating data repositories to store customers, products and orders.graphql-spring-boot-starter
for adding GraphQL Java support with Spring Boot.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.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
Code language: JSON / JSON with Comments (json)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 }
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
Code language: JSON / JSON with Comments (json)type Mutation { createOrder(order: CreateOrderInput!): Order! } input CreateOrderInput { customerId: ID! productId: ID! quantity: Int! }
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:
Code language: Bash (bash)./gradlew bootRun
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
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.