Early look at developing API with 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 Query
, Mutation
, 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
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