DEV Community

Cover image for GraphQL with Spring Boot
Arpan Bandyopadhyay
Arpan Bandyopadhyay

Posted on • Edited on

GraphQL with Spring Boot

What Is GraphQL?
Traditional REST APIs work with the concept of Resources that the server manages. These resources can be manipulated in some standard ways, following the various HTTP verbs. This works very well as long as our API fits the resource concept, but quickly falls apart when we need to deviate from it.

This also suffers when the client needs data from multiple resources at the same time. For example, requesting a blog post and the comments. Typically, this is solved by either having the client make multiple requests or by having the server supply extra data that might not always be required, leading to larger response sizes.

GraphQL offers a solution to both of these problems. It allows for the client to specify exactly what data is desired, including from navigating child resources in a single request, and allows for multiple queries in a single request.

Here I am going to discuss how to integrate GraphQL with Spring boot.

I am going to use below to achieve this :

  1. Spring boot libraries
  2. GraphQL libraries
  3. Spring JPA
  4. Hibernate
  5. Lombok
  6. Postgres

What is GraphiQL?
GraphQL also has a companion tool called GraphiQL. This is a UI that is able to communicate with any GraphQL Server and execute queries and mutations against it

GraphQL Server with Connected Database
This architecture has a GraphQL Server with an integrated database and can often be used with new projects. On the receipt of a Query, the server reads the request payload and fetches data from the database. This is called resolving the query. The response returned to the client adheres to the format specified in the official GraphQL specification.

Image description

In the above diagram, GraphQL server and the database are integrated on a single node. The client (desktop/mobile) communicates with GraphQL server over HTTP. The server processes the request, fetches data from the database and returns it to the client.

Here is the full list of dependencies , I have used to work with GraphQL & Spring Boot

implementation group: 'io.leangen.graphql', name: 'graphql-spqr-spring-boot-starter', version: '0.0.6'
implementation group: 'org.postgresql', name: 'postgresql', version: '42.2.20'
implementation 'org.apache.commons:commons-collections4:4.4'
implementation 'org.jdal:jdal-core:2.0.0'
implementation group: 'com.graphql-java', name: 'graphql-java-extended-scalars', version: '2021-06-29T01-19-32-8e19827'
implementation group: 'org.hibernate', name: 'hibernate-core', version: '5.5.2.Final'
    implementation group: 'commons-net', name: 'commons-net', version: '3.8.0'
    implementation group: 'com.graphql-java-kickstart', name: 'graphiql-spring-boot-starter', version: '7.1.0'
    compileOnly group: 'org.projectlombok', name: 'lombok', version: '1.18.20'
    implementation group: 'org.apache.commons', name: 'commons-collections4', version: '4.4'
    implementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.12.3'
    implementation group: 'com.graphql-java-kickstart', name: 'graphql-spring-boot-starter', version: '7.0.1'
    implementation group: 'org.springframework', name: 'spring-web', version: '5.3.6'
    implementation group: 'com.graphql-java-kickstart', name: 'graphql-spring-boot-starter-test', version: '7.0.1'
    implementation group: 'org.springframework.boot', name: 'spring-boot-starter-data-jpa', version: '2.5.0'
    implementation group: 'commons-io', name: 'commons-io', version: '2.10.0'
    compileOnly group: 'org.hibernate.orm', name: 'hibernate-jpamodelgen', version: '6.0.0.Alpha6'
    implementation group: 'org.modelmapper', name: 'modelmapper', version: '2.4.4'
    implementation group: 'org.postgresql', name: 'postgresql', version: '42.2.20'
    implementation group: 'com.graphql-java-kickstart', name: 'playground-spring-boot-starter', version: '5.10.0'
    implementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.12.0'
    compileOnly 'org.hibernate:hibernate-jpamodelgen'
    annotationProcessor('org.hibernate:hibernate-jpamodelgen')
    implementation 'org.springframework.boot:spring-boot-starter'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    compileOnly 'org.projectlombok:lombok'
    annotationProcessor 'org.projectlombok:lombok'
Enter fullscreen mode Exit fullscreen mode

My Sample Project Structure

Image description

My Entity Classes

  1. Employee.java
package com.example.demo.entities;

import io.leangen.graphql.annotations.GraphQLNonNull;
import io.leangen.graphql.annotations.types.GraphQLType;
import lombok.Data;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import java.math.BigInteger;
import java.time.LocalDate;

@Entity
@Data
@GraphQLType
@Table(name = "employee")
public class Employee {

    @Id
    @Column(name = "emp_id")
    private BigInteger employeeId;
    @Column(name = "ename")
    @GraphQLNonNull
    private String employeeName;
    @Column(name = "email_id")
    private String email;
    @Column(name = "city")
    private String city;
    @Column(name = "country")
    private String country;
    @Column(name = "dept_id")
    private Integer deptId;
    @Column(name = "dob")
    private LocalDate dob;

    @OneToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "dept_id", insertable = false, updatable = false)
    private Department department;
}
Enter fullscreen mode Exit fullscreen mode

2. Department.java

package com.example.demo.entities;

import io.leangen.graphql.annotations.GraphQLQuery;
import io.leangen.graphql.annotations.types.GraphQLType;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import java.util.List;

@Entity
@Data
@AllArgsConstructor
@NoArgsConstructor
@GraphQLType(description = "This is Department")
@Table(name = "department")
public class Department {

    @Id
    @Column(name = "dept_id")
    @GraphQLQuery(description = "This is deptID")
    private Integer deptId;
    @Column(name = "dname")
    private String deptName;

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "department")
    private List<Employee> employee;
}
Enter fullscreen mode Exit fullscreen mode

My Repositories **
**1. DepartmentDao.java

package com.example.demo.dao;

import com.example.demo.entities.Department;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository
public interface DepartmentDao extends CrudRepository<Department, Integer> {

    public Department findByDeptName(String deptName);
}
Enter fullscreen mode Exit fullscreen mode

2.EmployeeDao.java

package com.example.demo.dao;

import com.example.demo.entities.Employee;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;

import java.math.BigInteger;
import java.util.List;

@Repository
public interface EmployeeDao extends CrudRepository<Employee, BigInteger> {
    public List<Employee> findByEmployeeName(String ename);
}
Enter fullscreen mode Exit fullscreen mode

My Service classes
1.EmployeeService.java

package com.example.demo.service;

import com.example.demo.dao.EmployeeDao;
import com.example.demo.entities.Employee;
import com.example.demo.entities.EmployeeDto;
import com.example.demo.exception.ResourceNotFoundException;
import org.apache.commons.collections4.IteratorUtils;
import org.apache.commons.lang.RandomStringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigInteger;
import java.util.List;

@Service
public class EmployeeService {

    @Autowired
    private EmployeeDao employeeDao;


    public List<Employee> getAllEmployee() {
        return IteratorUtils.toList(employeeDao.findAll().iterator());
    }

    public Employee getEmployeeByID(BigInteger eid) {
        return employeeDao.findById(eid).orElseGet(() -> {
            throw new ResourceNotFoundException("No Employee found");
        });
    }

    public List<Employee> getEmployeeByName(String ename) {
        return employeeDao.findByEmployeeName(ename);
    }

}
Enter fullscreen mode Exit fullscreen mode

2. DepartmentService.java

package com.example.demo.service;

import com.example.demo.dao.DepartmentDao;
import com.example.demo.entities.Department;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class DepartmentService {

    @Autowired
    private DepartmentDao departmentDao;

    public Department getDeptByName(String deptName) {
        return departmentDao.findByDeptName(deptName);
    }
}
Enter fullscreen mode Exit fullscreen mode

My Query classes
1.EmployeeQuery.java

package com.example.demo.queries;

import com.example.demo.entities.Employee;
import com.example.demo.service.EmployeeService;
import io.leangen.graphql.annotations.GraphQLArgument;
import io.leangen.graphql.annotations.GraphQLQuery;
import io.leangen.graphql.spqr.spring.annotations.GraphQLApi;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.math.BigInteger;
import java.util.List;

@Component
@GraphQLApi
public class EmployeeQuery {

    @Autowired
    private EmployeeService employeeService;

    @GraphQLQuery(description = "This query is used to fetch all Employee ")
    public List<Employee> searchAllEmployee() {
        return employeeService.getAllEmployee();
    }

    @GraphQLQuery(description = "This query is used to fetch Employee by employeeId")
    public Employee searchEmployeeById(@GraphQLArgument(name = "employeeId") BigInteger eid) {
        return employeeService.getEmployeeByID(eid);
    }

    @GraphQLQuery(description = "This query is used to fetch employeeId by employeeName")
    public List<Employee> searchEmployeeByName(@GraphQLArgument(name = "employeeName") String eName) {
        return employeeService.getEmployeeByName(eName);
    }
}
Enter fullscreen mode Exit fullscreen mode

2.DepartmentQuery.java

package com.example.demo.queries;

import com.example.demo.entities.Department;
import com.example.demo.service.DepartmentService;
import io.leangen.graphql.annotations.GraphQLArgument;
import io.leangen.graphql.annotations.GraphQLQuery;
import io.leangen.graphql.spqr.spring.annotations.GraphQLApi;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
@GraphQLApi
public class DepartmentQuery {

    @Autowired
    private DepartmentService departmentService;

    @GraphQLQuery(description = "This query is used to fetch department by department name")
    public Department searchDeptByName(@GraphQLArgument(name = "deptName") String deptName) {
        return departmentService.getDeptByName(deptName);
    }
}
Enter fullscreen mode Exit fullscreen mode

*Now we can see the graphql schema documentation after starting the server : *

URL : http://localhost:8080/graphiql

Image description

Image description

Image description

Execute few of queries

Image description

Image description

Here I have shared all the steps to integrate Spring boot with graphQL. I will come with other topics in my coming posts.

Thank you..
Arpan

Let's connect:
LinkedIn : https://www.linkedin.com/in/arpan-bandyopadhyay-bb5b1a54/

Top comments (3)

Collapse
 
satindersidhu profile image
Satinder Sidhu

This is very helpfull & very well described

Collapse
 
arpanforgeek profile image
Arpan Bandyopadhyay

Thank you @satindersidhu

Collapse
 
soscarlos profile image
Carlos Sosa

Hallo Arpan! how did you handled the LocalDate type? did you have to use a scalar?