Spring Boot and GraphQL

Spring Boot and GraphQL thumbnail
13K
By Dhiraj Ray 10 October, 2020

In this article, we will be discussing how we can implement GraphQL in Java and integrate it with a real-time Spring boot project from scratch. The project will be a maven based project having spring data integrated to fetch data from MySQL database.

We will implement multiple GraphQL data fetchers which will inturn utilize spring data JpaRepository to query the DB.

What is GraphQL

GraphQL is a specification to fetch data from the server to the client over an HTTP call. The specification describes how to ask for data and rather than asking the whole bunch of data at once, it specifies to ask only the required data that would be meaningful to a client. It is just an alternative to REST in some way.

For example, we have a GET employee REST API that returns the details of an employee. There could be 50 different fields associated with an employee such as name, DOB, address, level, salary, age, etc but sending all these fields in the response of a REST call does not make sense and it could be difficult to determine what are the fields that should be actually sent in the response and tomorrow the requirement might change and we might require to add some more fields.

In some situation, we start versioning of the API and it becomes difficult to maintain those APIs.

With GraphQL, there is a single entry point that takes care of all kinds of client Query and the client can decide which Query needs to be executed that could be to either fetch employee details or to fetch any department details or it could be any other app info over the same entry point and even the client can decide what are the attributes that he is interested in.

For example the same endpoint http://localhost:8080/api/graphql/dept can be used to fetch all department lists or find a single department based on ID with different queries (payloads).

Fetch all Departments(only name and description)
{
 allDepts{
     name
     description
 }   
}
Fetch deprtment By ID(name and employee details only)
 {
   deptById(id: 1) {
        name
	emps
   }
 }

For both the payloads, the endpoint remains the same whereas the request and response differ.

While fetching department details sometimes it might be useful to fetch the name and description of a department whereas in some cases we might be only interested in knowing the employee's details of a particular department and this could be easily achieved through GraphQL.

Difference Between GraphQL and REST

  • There is a single entry point in GraphQL whereas there are multiple entry points in case of REST APIs.
  • GraphQL is schema based.
  • GraphQL could be faster then REST.
  • Exisiting REST APIs can be wrapped with GraphQL.

Spring Boot and GraphQL Project Setup

Let us quickly set up our spring boot and GraphQL project with maven dependencies in place. Head over to https://start.spring.io/ and generate a sample spring boot app with maven dependencies spring-web, spring-data, Lombok and HyperSQL DB. We will first use in-memory DB and then integrate MYSQL with it.

spring-boot-graphql-project-structure

Now, we will manually add GraphQL related maven dependencies in our pom.xml. Below are those dependencies that we have added to our pom file.

<dependency>
	<groupId>com.graphql-java</groupId>
	<artifactId>graphql-java</artifactId>
	<version>14.0</version>
</dependency>
<dependency>
	<groupId>com.graphql-java</groupId>
	<artifactId>graphql-java-spring-boot-starter-webmvc</artifactId>
	<version>2.0</version>
</dependency>
<dependency>
	<groupId>com.google.guava</groupId>
	<artifactId>guava</artifactId>
	<version>26.0-jre</version>
</dependency>

graphql-java is the Java (server) implementation for GraphQL for executing queries where as graphql-java-spring-boot-starter-webmvc for exposing the API via Spring Boot over HTTP.

Spring Boot Controller and Service Implementation

We have our project setup done and now let us create our basic setup such as model class implementation, spring data repository setup and controller class implementation.

Model Class Implementation

We will have 2 model classes - Department.java and Employee.java. Department and employee will have a one-many relationship. Below are the class implementation.

Department.java
@AllArgsConstructor
@NoArgsConstructor
@Setter
@Getter
@Entity
@Table
public class Department {

    @Id
    private int id;
    private String name;
    private String description;

    @OneToMany
    private List emps;
}

Employee.java
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
@Entity
@Table
public class Employee {

    @Id
    private int id;
    private String name;
    private int age;
    private double salary;
}

Now let us define our repository class. This extends spring data JpaRepository.

public interface DepartmentRepo extends JpaRepository<Department, Integer> {
}
EmployeeRepo.java
public interface EmployeeRepo extends JpaRepository<Employee, Integer> {
}

Now let us define our controller class. As discussed above, in GraphQL we have a single entry point and hence the controller class contains only one endpoint that takes a string quey as as a body input. We will discuss the GraphQLProvider in the below sections.

@RestController
@RequestMapping("/api")
public class DepartmentController {

    @Autowired
    private GraphQLProvider graphQLProvider;

    @PostMapping("/graphql/dept")
    public ResponseEntity<Object> listDepartments(@RequestBody String query){
        ExecutionResult execute = graphQLProvider.graphQL().execute(query);

        return new ResponseEntity<>(execute, HttpStatus.OK);
    }

}

Creating GraphQL Java Server

Creating a GraphQL Java server has 2 steps.

  • Defining a GraphQL Schema.
  • Data fetchers implementation regarding how the actual data for a query is fetched.

Defining GraphQL Schema

The GraphQL schema file describes the type of query that can be performed by the client and the different fields that can be sent in the response. Below is our schema file created inside src/main/resources

dept.graphql
schema {
    query: Query
}

type Query{
    allDepts: [Department]
    deptById(id: Int) : Department
}

type Department {
    id: Int
    name: String
    description: String
    emps: [Employee]
}

type Employee {
    id: Int
    name: String
    age: Int
    salary: Float
}

The client can perform 2 different queries as allDepts and deptById and we have defined the different fields that a client can expect in the response. Now, the client has to decide what are the fields that he is interested in.

A sample request from the client is given below where a client is only interested in name of the departments.

{
 allDepts{
     name
 }   
}

GraphQL Data Fetchers Implementation

Data fetchers are the implementation for fetching the actual data that can be either from a DB or any third party API.

Our data fetcher implementation returns a DataFetcher implementation which takes a DataFetcherEnvironment as a parameter and this DataFetcherEnvironment is used to fetch the request parameter.

We also have a simple implementation to load the data into the DB during application startup.

@Component
public class GraphQLDataFetchers {

    @Autowired
    private DepartmentRepo departmentRepo;

    @Autowired
    private EmployeeRepo employeeRepo;

    public DataFetcher getDeptByIdDataFetcher() {
        return dataFetchingEnvironment -> {
            int deptId = dataFetchingEnvironment.getArgument("id");
            return departmentRepo.findById(deptId);
        };
    }

    public DataFetcher getAllDepartmentsDataFetcher() {
        return dataFetchingEnvironment -> departmentRepo.findAll();
    }

    public DataFetcher findEmployeesByDept() {
        return dataFetchingEnvironment -> {
            int deptId = dataFetchingEnvironment.getArgument("id");
            Department department = departmentRepo.findById(deptId).get();
            return department.getEmps();
        };
    }

    @PostConstruct
    public void loadDb(){
        Employee employee = employeeRepo.save(new Employee(1, "Dhiraj", 20, 3456));
        Stream.of(
                new Department(1, "Computer Science", "Computer Science department", Stream.of(employee).collect(Collectors.toList())))
                .forEach(department -> departmentRepo.save(department));
    }
}

Now, we have done our schema defined and our data fetchers are ready and now we have to implement the wiring between the schema file and the data fetchers so that the GraphQL can know which data fetcher to be executed based on the query requested by the client.

GraphQL Providers

GraphQL providers act as a wiring between the schema file and the data fetchers that we created above.

The spring bean GraphQL is required by GraphQL Java Spring adapter will to make our schema available via HTTP on the default url /graphql.

TypeDefinitionRegistry is the parsed version of our schema file that was loaded from the classpath with the Guava library.

SchemaGenerator combines the TypeDefinitionRegistry with RuntimeWiring to actually make the GraphQLSchema.

The buildWiring() actually wires the query and the data fetchers.

@Component
public class GraphQLProvider {

    private GraphQL graphQL;

    @Autowired
    GraphQLDataFetchers graphQLDataFetchers;

    @Autowired
    private AllDeptDataFetchers allDeptDataFetchers;

    @Bean
    public GraphQL graphQL() {
        return graphQL;
    }

    @PostConstruct
    public void init() throws IOException {
        URL url = Resources.getResource("dept.graphql");
        String sdl = Resources.toString(url, Charsets.UTF_8);
        GraphQLSchema graphQLSchema = buildSchema(sdl);
        this.graphQL = GraphQL.newGraphQL(graphQLSchema).build();
    }

    private GraphQLSchema buildSchema(String sdl) {
        TypeDefinitionRegistry typeRegistry = new SchemaParser().parse(sdl);
        RuntimeWiring runtimeWiring = buildWiring();
        SchemaGenerator schemaGenerator = new SchemaGenerator();
        return schemaGenerator.makeExecutableSchema(typeRegistry, runtimeWiring);
    }

    private RuntimeWiring buildWiring() {
        return RuntimeWiring.newRuntimeWiring()
                .type("Query", typeWiring -> typeWiring
			.dataFetcher("allDepts", graphQLDataFetchers.getAllDepartmentsDataFetcher())
                        //.dataFetcher("allDepts", allDeptDataFetchers)
                        .dataFetcher("deptById", graphQLDataFetchers.getDeptByIdDataFetcher()))
                .build();
    }

}

This is pretty much the immplenetation of GraphQL with Spring Boot. Next, let us test our app.

Spring Boot GraphQL App Testing

To test the app, let us simply run the SpringBootGraphqlApplication.java as a java application and then we can make some sample requests as below:

Fetch Department List with Employee

{
 allDepts{
     name
     description
     emps{
         name
     }
 }   
}
spring-boot-graphql-fetch-all

Fetch Department By Id

 {
   deptById(id: 1) {
        name
        description
   }
 }
spring-boot-graphql-fetch-one

Combined Department and Employee Request

 {
 allDepts{
     name
     description
     emps {
         name
     }
 }   
   deptById(id: 1) {
        name
        description
   }
 }
spring-boot-graphql-combined-api-request

We can also create our data fetchers by implementing DataFetcher interface as below but make sure you wire the same in our GraphQL provider implementation.

@Component
public class AllDeptDataFetchers implements DataFetcher<List<Department>> {

    @Autowired
    private DepartmentRepo deptRepo;

    @Override
    public List get(DataFetchingEnvironment dataFetchingEnvironment) throws Exception {
        return deptRepo.findAll();
    }
}

Conclusion

In this article, we discussed how we can implement GraphQL in Java and integrate it with a real-time Spring boot project from scratch. Next, we can explore how exception handling can be done in this implementation.

Share

If You Appreciate This, You Can Consider:

We are thankful for your never ending support.

About The Author

author-image
A technology savvy professional with an exceptional capacity to analyze, solve problems and multi-task. Technical expertise in highly scalable distributed systems, self-healing systems, and service-oriented architecture. Technical Skills: Java/J2EE, Spring, Hibernate, Reactive Programming, Microservices, Hystrix, Rest APIs, Java 8, Kafka, Kibana, Elasticsearch, etc.

Further Reading on Spring Boot