Early look at developing API with Spring GraphQL

Spring GraphQL

In this blog post we will see how to develop GraphQL API with Spring GraphQL (graphql-spring-boot-starter) library.

Previously, Spring did not have own library support for developing GraphQL API, we had to integrate with third-party libraries like NetFlix’s DGS Framework, GraphQL Java Spring . Earlier in my blog posts, I have written blog posts ( part-1, part-2, part-3, part-4 ) for developing GraphQL API with NetFlix’s DGS Framework

Now Spring team is developing its own GraphQL library based on the GraphQL Java Spring project.

I am going to demonstrate GraphQL API development with spring-boot project

Note

As of writing this blog post, the Spring GraphQL project is currently in MileStone-2 stage. There might be minor changes in the final release.

Setting up the project

To create a project, go to https://start.spring.io and select following starter project.

  • SpringBoot version 2.5.x
  • spring-boot-starter-web
  • spring-boot-starter-data-jpa
  • h2

Since Spring GraphQl library is in Milestone stage, to use the library we need to add the milestone repositories first in pom.xml

<repositories> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/milestone</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories> <pluginRepositories> <pluginRepository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/milestone</url> <snapshots> <enabled>false</enabled> </snapshots> </pluginRepository> </pluginRepositories>
Code language: XQuery (xquery)

Now add Spring Graphql dependency

<dependency> <groupId>org.springframework.experimental</groupId> <artifactId>graphql-spring-boot-starter</artifactId> <version>1.0.0-M2</version> </dependency>
Code language: Java (java)

We also need to add the following dependency to support additional custom scalar types

<dependency> <groupId>com.graphql-java</groupId> <artifactId>graphql-java-extended-scalars</artifactId> <version>17.0</version> </dependency>
Code language: Java (java)

If you want to support GraphQL subscription operations, add the following dependency.

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency>
Code language: Java (java)

Download the project and import it into your favourite IDE.

Schema

By default, GraphQL schema files are expected to be in src/main/resources/graphql and have the extension “.graphqls”, “.graphql”, “.gql”, or “.gqls”. You can customize the schema locations to check as follows:

spring.graphql.schema.locations=classpath:graphql/
Code language: Apache (apache)

The GraphQL schema can be viewed over HTTP at “/graphql/schema”. This is not enabled by default:

spring.graphql.schema.printer.enabled=false
Code language: Java (java)

GraphQL schemas are compatible across libraries,if you are using only default data scalar types.

I am going to use a slimmed down schema version from one of the previous project for developing API for this example.

type Employee { id:Int! first_name: String! last_name: String! gender: Gender birth_date: Date hire_date: Date department: Department } type Department { id : Int! name : String! employees:[Employee!] } enum Gender { M F } scalar Date input SubmittedEmployee { first_name: String! last_name: String! gender: Gender birth_date: Date hire_date: Date deptId:Int! } input SubmittedDepartment { name : String! } type Query { employee(id : Int) : Employee employees :[Employee!] departments :[Department!] getEmployeesByDeptId(deptId : Int) :[Employee!] } type Mutation { createDepartment(department : SubmittedDepartment) :Department createEmployee(employee : SubmittedEmployee) : Employee updateEmpDepartment(emp_id: Int! , dept_id : Int!) : Boolean } type Subscription { notifyEmployeeCreation : Employee }
Code language: AngelScript (angelscript)

Registering custom scalar types

In our schema we are using Date custom scalar type. So first we need to register this type.

The GraphQL Java RuntimeWiring.Builder can be used to register. This class also can be used to register DataFetchers, type resolvers.

@SpringBootApplication public class SpringbootGraphqlDemoApplication implements RuntimeWiringConfigurer { .... @Override public void configure(RuntimeWiring.Builder builder) { builder.scalar(ExtendedScalars.Date); } }
Code language: Java (java)

Annotated Controllers

Spring GraphQL provides an annotation-based programming model where @Controller components use annotations to declare handler methods with flexible method signatures to fetch the data for specific GraphQL methods. For example:

@Controller public class EmployeeGraphQLController { @Autowired EmployeeService employeeService; @SchemaMapping(typeName = "Query" , field = "employees") public List<Employee> getAllEmployees() { return employeeService.getAllEmployees(); } }
Code language: Java (java)

@SchemaMapping – The @SchemaMapping annotation maps a handler method to a method in the GraphQL schema and declares it to be the DataFetcher for that field. 

typeName – Specifies kind of operation . Possible values are Query, Mutation,Subscription

field – Specifies method name in schema

If you specify the only typeName property in @SchemaMapping ,field name defaults to the method name

The above method can also be written as below

@SchemaMapping(typeName = "Query") public List<Employee> employees() { return employeeService.getAllEmployees(); }
Code language: Java (java)

There are shortcut annotations  for Query, Mutation, and Subscription types.

@QueryMapping@MutationMapping, and @SubscriptionMapping are meta annotations that are themselves annotated with @SchemaMapping and have the typeName preset to QueryMutation, or Subscription respectively.

For example

@Controller public class EmployeeGraphQLController { @QueryMapping public Employee employee(@Argument Integer id) { .... } @MutationMapping public boolean updateEmpDepartment(@Argument Integer emp_id ,@Argument Integer dept_id) { ... } @SubscriptionMapping public Publisher<Employee> notifyEmployeeCreation() { .... } }
Code language: Java (java)

If the controller method name differs with schema method name, you can specify the binding with value attribute

@QueryMapping(value = "employee") public Employee getEmployeeById(@Argument Integer id) { .... }
Code language: Java (java)

Testing API with GraphiQL

Spring GraphQL includes GraphiQL GUI client for testing the API. GraphiQL client can be accessed at

http://localhost:8080/graphiql

GraphiQL client can be configured as follows

spring.graphql.graphiql.enabled=true 
spring.graphql.graphiql.path=/graphiql
GraphiQL client

GraphQL Subscriptions

The GraphQL WebSocket endpoint supports WebSocket handshakes at “/graphql” by default. The below shows the properties that apply for WebSocket handling:

spring.graphql.websocket.path=/graphql

# Time within which a "CONNECTION_INIT" message must be received from the client
spring.graphql.websocket.connection-init-timeout=60s

The GraphQL WebSocket endpoint is off by default. To enable it:

  • Add spring.graphql.websocket.path=/graphql property in application.properties file
  • For a Servlet application, add the WebSocket starter dependency in pom.xml file
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency>
Code language: Java (java)
  • For a WebFlux application, set the spring.graphql.websocket.path application property.
 spring.graphql.websocket.path=/graphql 

Testing GraphQL subscriptions

We can test subscriptions using Altair GraphQL client.

Click on subscription icon to set the Websocket URL

Set WebSocket URl and select subscription-type WebSocker(graphql-ws)

Now run the subscription query

Run the createEmployee mutation

Now we should receive the subscription notification

You can download source code for this blog post from GitHub

Similar Posts