Basic Java GraphQL Tutorial
Creating a GraphQL API using the Spring Boot SPQR in a matter of minutes
Note: This is a basic example/use-case for GraphQL in Spring Boot/Java. More in-depth, advanced tutorials will be coming in the future. React to this post & comment below for any suggestions.
This tutorial regards that you already know the very basics of Java, Spring Boot and Gradle, as well as how to bootstrap a new Spring Boot Gradle Project. This project uses JDK 11.
Install Dependencies
These are the dependecies needed for your build.gradle
compileOnly "org.projectlombok:lombok:1.18.12"
annotationProcessor "org.projectlombok:lombok:1.18.12"
testCompileOnly "org.projectlombok:lombok:1.18.12"
testAnnotationProcessor "org.projectlombok:lombok:1.18.12"
/* ---------- This is what is needed for GraphQL ---------- */
implementation "io.leangen.graphql:graphql-spqr-spring-boot-starter:0.0.6"
implementation group: 'org.postgresql', name: 'postgresql', version: '42.2.23'
implementation "org.hibernate.validator:hibernate-validator:7.0.1.Final"
implementation 'org.springframework.boot:spring-boot-starter'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
Building Entities
In this example, I will have an Employee
Entity, which has a Many-To-One Relationship with the Company
Entity. These are all standard JPA Entities, but with a few SPQR Annotations on them.
@Setter
@Getter
@Entity
@NoArgsConstructor
@AllArgsConstructor
public class Employee {
@Id
@GeneratedValue
@Column(columnDefinition = "uuid", updatable = false)
@GraphQLQuery(name = "id", description = "A Person's Id")
private UUID id;
@NotNull(message = "There must be a Person's Name!")
@GraphQLQuery(name = "fullName", description = "A Person's Name")
private String fullName;
@NotNull(message = "A Person must have an Age!")
@GraphQLQuery(name = "age", description = "A Person's Age")
private int age;
@ManyToOne(
targetEntity = Company.class,
fetch = FetchType.LAZY,
cascade = CascadeType.ALL)
@GraphQLQuery(
name = "company",
description = "The Company a Person works for")
private Company company;
}
And the Company:
@Setter
@Getter
@Entity
@NoArgsConstructor
@AllArgsConstructor
public class Company {
@Id
@GeneratedValue
@Column(columnDefinition = "uuid", updatable = false)
@GraphQLQuery(name = "id", description = "A Company's Id")
private UUID id;
@NotNull(message = "There must be a Company Name!")
@GraphQLQuery(
name = "name",
description = "A Company's Name")
private String name;
@Min(10000)
@NotNull(message = "You gotta tell us how rich you are!")
@GraphQLQuery(
name = "balance",
description = "A Company's Dollar")
private BigDecimal balance;
@NotNull(message = "There must be a Company Type!")
@GraphQLQuery(
name = "type",
description = "The type of company")
private CompanyType type;
public enum CompanyType {
PRIVATE_LIMITED,
SOLE_TRADER,
PUBLIC
}
}
Next, for our JPA Entities to have Data Access Layers (for persisting into a DB), we must create DAOs for each Entity.
Here is the Company DAO:
@Repository
public interface CompanyDao extends JpaRepository<Company, UUID> {
/* Any custom JPA methods here */
}
And the Employee DAO:
@Repository
public interface EmployeeDao extends JpaRepository<Employee, UUID> {
List<Employee> findAllByCompanyIdIn(Set<UUID> companyIds);
}
Now here's the bit where SPQR really shines, what would be "Controllers" in a standard Spring Web REST API, are now replaced with neat little resolvers which are exposed as mutations/queries/subscriptions by simply annotating it with @GraphQLApi
at class-level.
Here's what the resolvers for both Entities would look like:
@Service
@GraphQLApi
@Transactional
@RequiredArgsConstructor
public class CompanyResolver {
private final CompanyDao companyDao;
@GraphQLQuery(name = "getAllCompanies")
public List<Company> getAllCompanies() {
return companyDao.findAll();
}
@GraphQLMutation(name = "saveCompany")
public Company saveCompany(
@GraphQLArgument(name = "company") @GraphQLNonNull
@Valid Company company) {
return companyDao.save(company);
}
@GraphQLMutation(name = "deleteCompany")
public void deleteCompany(
@GraphQLArgument(name = "id") @GraphQLNonNull
@Valid UUID companyId) {
companyDao.deleteById(companyId);
}
}
@Service
@GraphQLApi
@Transactional
@RequiredArgsConstructor
public class EmployeeResolver {
private final EmployeeDao employeeDao;
@GraphQLQuery(name = "getAllEmployees")
public List<Employee> getAllEmployees() {
return employeeDao.findAll();
}
@GraphQLQuery(name = "getEmployeeById")
public Optional<Employee> getEmployeeById(
@GraphQLArgument(name = "id") @GraphQLNonNull @Valid UUID id) {
return employeeDao.findById(id);
}
@GraphQLMutation(name = "saveEmployee")
public Employee saveEmployee(@GraphQLArgument(name = "employee")
@GraphQLNonNull @Valid Employee company) {
return employeeDao.save(company);
}
@GraphQLMutation(name = "deleteEmployee")
public void deleteEmployee(@GraphQLArgument(name = "id") @GraphQLNonNull
@Valid UUID companyId) {
employeeDao.deleteById(companyId);
}
}
Finally, we wanna make sure our application is configured properly before running, so lets add this to our /resources/application.yml
. (Making sure to substitute the username/passwords for the DB, and the server port if needed.)
Note: If you have bootstrapped your project and this file either doesn't exist or has the wrong file extension, delete all files inside the resources
directory and create an application.yml
.
server:
port: 9090
http2:
enabled: true
spring:
jpa:
open-in-view: true
show_sql: true
hibernate:
ddl-auto: update
datasource:
url: "jdbc:postgresql://localhost:5432/spqr-test"
username: "username"
password: "password"
driver-class-name: org.postgresql.Driver
graphql:
spqr:
gui:
enabled: true
voyager:
enabled: true
These properties will allow the GraphQL Playground to operate on http://localhost:9090/gui
, where you can explore your newly-created GraphQL API and make some requests!
If you have any questions or errors, be sure to comment below!
Happy GraphQLing!!