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 :
- Spring boot libraries
- GraphQL libraries
- Spring JPA
- Hibernate
- Lombok
- 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.
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'
My Sample Project Structure
My Entity Classes
- 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;
}
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;
}
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);
}
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);
}
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);
}
}
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);
}
}
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);
}
}
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);
}
}
*Now we can see the graphql schema documentation after starting the server : *
URL : http://localhost:8080/graphiql
Execute few of queries
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)
This is very helpfull & very well described
Thank you @satindersidhu
Hallo Arpan! how did you handled the LocalDate type? did you have to use a scalar?