Getting started with GraphQL in Java Spring-Boot. Part — 1

Introduction

GraphQL is a query language for your API. It gives the client a flexibility to fetch only the data it needs.

Imagine, your backend behaves like a database and you can query the backend the way you need. Thats exactly what GraphQL offers you, a way to fetch data flexibly from the backend. Just like you define a schema in the database you need to define the schema in the backend and it’s immediately available for the client (for example the front-end app) to query. The data source, the place where you fetch the data is managed by the backend application. So, your data layer is abstracted and where you fetch the data from still is decided by the backend application and GraphQL does not care about it. So, for instance you can fetch half of your data from database and rest from another API.

Note : GraphQL does not provide any storage mechanism. It only abstracts the backend and provides flexibility to the client.

GraphQL helps to create a service from the backend by defining schemas and allows the clients to access the data with the help of queries. Client can choose what data to fetch and thus reduce the amount of data fetched.

In the Rest API the client does not have control over what data to fetch. For example if there is an API which provides the details of all the students like name, college, sections, course etc. And client needs only the name of the student, it still gets all the other details with it. In GraphQL client can ask for just the name and it gets back only the name.

GraphQL has its own schema definition language (SDL), which you can refer to it here. It allows you to define the schema from the backend and also to express the types of each field it contains. A simple example of schema is defined below.

Example :type Phone {  
model:String!
price:Float
}

The SDL also allows to nest the schema as well. This is the powerful feature of GraphQL and allows you to represent the backend models in the graph like structure. Like the one below.

type Student {  
id:Int
name:String!
age:Int
}
type College {
name:String!
students:[
Student
]
}

In the above example the Student is used in the definition of College and can be queried to fetch the details about the students of the College.

GraphQL Query lets you retrieve the data from the backend to the client application, in other words it is the way of accessing the data in GraphQL.

Simple example of defining a query is given below. This query definition is done on the backend side.

Query { 
getStudent(id: Int):Student
}

Now, from the client side to query the backend you can start using below query.

query {  
getStudent(id:1) {
name
}
}

And below is the output from the above query. You requested for name and you get back just that.

{  
"data":{
"getStudent":{
"name":"Rob"
}
}
}

GraphQL with Spring-Boot

Let’s get started on implementing GraphQL with spring boot in Java. I am using Java 8 for this example.

We use three main libraries (grapqhl-spring-starter , grapqhl-spqr and graphiql-spring-boot-starter) that helps to implement GraphQL in spring boot very easily.

Since Java is a typed language, defining a model in Java and schema in GraphQL is a duplication and updating one leads to change in another. GrapQL spqr gives you the convenience of defining the schema using Java Class and automatically converts it to SDL for you.

Graphql-spqr helps to convert the Java model class into SDL. This avoids duplication of schemas in Java class and SDL

In this example I am taking College and Students as an example.

Now let us define the Student Class as below.

public class Student {

public int id;
public String name;

public Student(int id, String name) {
this.id = id;
this.name = name;
}

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}

and here is the College class.

public class College {

private int id;
private String name;

public College(int id, String name) {
this.id = id;
this.name = name;
}

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}

Let us define the Service classes in Java that can fetch the data for the above classes.

CollegeService class : To fetch the college details.

package com.graphql.service;

import com.graphql.schema.College;
import org.springframework.stereotype.Service;

@Service
public class CollegeService {

public College getCollegeById(int id) {
return new College(id, "Stanford");
}
}

StudentService class : To fetch student details.

package com.graphql.service;

import com.graphql.schema.Student;
import org.springframework.stereotype.Service;

import java.util.Arrays;
import java.util.List;

@Service
public class StudentService {

public Student getStudentById(int id) {
return new Student(id, "Rob");
}

public List<Student> getStudentsByCollegeId(int id) {
return Arrays.asList(new Student(1, "Rob"), new Student(2, "Mark"));
}
}

Now that we have defined the model/schema and the service to fetch the details. It’s time to define the GraphQL query in the backend. The query lets you access the data from the frontend.

GraphQL-Spqr makes it easy for you to define the query by using an Annotation.

CollegeQuery:

Notice the GraphQLContext part in the below class. That allows you to inject the students dependency into the college. Notice how there is a separation of how you fetch the college and how you can fetch the students. It’s two independent methods. The students query does not get invoked unless you ask for it which we can see later in the post when we query.

package com.graphql.queries;

import com.graphql.schema.College;
import com.graphql.schema.Student;
import com.graphql.service.CollegeService;
import com.graphql.service.StudentService;
import io.leangen.graphql.annotations.GraphQLArgument;
import io.leangen.graphql.annotations.GraphQLContext;
import io.leangen.graphql.annotations.GraphQLNonNull;
import io.leangen.graphql.annotations.GraphQLQuery;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.List;

@Component
public class CollegeQuery {

@Autowired CollegeService collegeService;

@Autowired StudentService studentService;

@GraphQLQuery(name = "getCollege") //Way to define query
@GraphQLNonNull
public College getCollegeById(@GraphQLNonNull @GraphQLArgument(name = "id") int id) {
return collegeService.getCollegeById(id);
}

@GraphQLQuery(name = "students")
public List<Student> getStudents(@GraphQLContext College college)// Notice the GraphQLContext part . That is used to inject the students into the college.
{
return studentService.getStudentsByCollegeId(college.getId());
}
}

StudentQuery:

package com.graphql.queries;

import com.graphql.schema.Student;
import com.graphql.service.StudentService;
import io.leangen.graphql.annotations.GraphQLArgument;
import io.leangen.graphql.annotations.GraphQLNonNull;
import io.leangen.graphql.annotations.GraphQLQuery;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class StudentQuery {

@Autowired StudentService studentService;

@GraphQLQuery(name = "getStudent")
public Student getStudentById(@GraphQLNonNull @GraphQLArgument(name = "id") int id) {
return studentService.getStudentById(id);
}
}

Now we have everything set, we just need to register the schema with the schema generator. It basically parses your class to get the GraphQL schema. Let’s define the configuration and we are set.

package com.graphql.config;

import com.graphql.queries.CollegeQuery;
import com.graphql.queries.StudentQuery;
import graphql.schema.GraphQLSchema;
import io.leangen.graphql.GraphQLSchemaGenerator;
import io.leangen.graphql.metadata.strategy.query.AnnotatedResolverBuilder;
import io.leangen.graphql.metadata.strategy.query.PublicResolverBuilder;
import io.leangen.graphql.metadata.strategy.value.jackson.JacksonValueMapperFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class GraphqlConfig {
//Autowire all the queries here.
@Autowired StudentQuery studentQuery;

@Autowired CollegeQuery collegeQuery;

@Bean
GraphQLSchema schema() {

GraphQLSchemaGenerator schemaGenerator =
new GraphQLSchemaGenerator()
.withResolverBuilders(
// Resolve by annotations
new AnnotatedResolverBuilder(),
// Resolve public methods inside root package
new PublicResolverBuilder("com.graphql"));
//Register all the queries here with the schemaGenerator.
schemaGenerator = schemaGenerator.withOperationsFromSingleton(studentQuery)
.withOperationsFromSingleton(collegeQuery);
return schemaGenerator.withValueMapperFactory(new JacksonValueMapperFactory()).generate();
}
}

GraphiQL spring boot dependency has an inbuilt ui library that lets you query the backend. It is accessible on /graphiql endpoint.

The ui looks like below. You can see all the queries available on the right side of the UI. If not open you can select the Docs link on the top right corner.

d

You can click on those queries and schemas and play around with it. You can see the types of College and Student and what the getCollege and getStudent query return.

If you click on College link you can see the students array in the schema. Though you did not define it in the College class it got injected automatically when you defined the College Query.

Notice how the students got injected from GraphQL context in the College schema.

Let’s try to query for the data and let’s see what we get back. Notice how we queried for name in the college query and we just got back name.

We queried for name from college and we got back just the name,

Let us now try to query for students and we should get back students in the results.

Fetch students from the college query.

In the above query you can see how we asked for id and name of the students and we just got back that. That’s the power of GraphQL, the frontend can ask for what it needs and you just get back that. So, from the backend perspective you need not fetch student details at all if its not needed. Notice how students fetching logic is in different method in Java and it is never called if it’s never asked. It’s a win-win situation in terms of performance in querying as well as data transfer perspective :).

You can play around with it and see how it works. It is available on github here.

If you liked the post, hit follow, as I will be coming up with more stories on GraphQL Mutations, GraphQL Dataloader, GraphQL Subscription, etc.

Search Engineer at Shutterstock.