DGS GraphQL and Spring Boot
This article explain how to use and implement DGS GraphQL using Spring Boot, and the GraphQL dashboard to make specific REST requests.
Join the DZone community and get the full member experience.
Join For FreeWhy GraphQL?
GraphQL provides a powerful layer to unify data from various backend sources, including databases, REST APIs, and microservices, into a single, consistent API. It enables the creation of flexible APIs that can evolve with changing client applications.
Introduction
This article will show you how to create an effective application that implements GraphQL using the Netflix DGS framework in the Spring Boot application.
Netflix DGS is an annotation-based GraphQL Java library built on top of Spring Boot. With Netflix DGS is allowed generating source code from GraphQL schemas. It simplifies writing unit tests and also supports websockets, file uploads, or GraphQL federation.
Tech Stack
- Java 17
- Spring Boot 3.4.3
- Netflix DGS GraphQL 10.0.4
- JPA
- Maven
- Lombok
- JUnit
- H2 Database
Application Development and Analysis
Create Spring Boot application using Spring Initializr. Setup is shown on the picture below:
In this picture we can see all dependencies which we need to include in the graphql-spring-boot project. For the database we will use in-memory H2 database. Here’s a list of required dependencies in Maven pom.xml:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.netflix.graphql.dgs</groupId>
<artifactId>graphql-dgs-spring-graphql-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.netflix.graphql.dgs</groupId>
<artifactId>graphql-dgs-spring-graphql-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
Grapql Schema
A schema may be defined in multiple graphqls files, but all of them have to be placed inside the /src/main/resources/schemas directory. The Netflix DGS library detects and loads them automatically.
"""
Sample user account container class.
"""
type User {
"Sequential identifier"
id: ID!
"User's first & last name"
name: String!
"User's email"
email: String
}
"""
User creation input
"""
input CreateUserInput {
"User's first & last name"
name: String
"User's email"
email: String
}
"""
User modification input
"""
input UpdateUserInput {
"User's first & last name"
name: String
"User's email"
email: String
}
"""
User object queries
"""
type Query {
user(id: ID!): User
users: [User]!
}
"""
User object modifications
"""
type Mutation {
createUser(input: CreateUserInput!): User!
updateUser(id: ID!, input: UpdateUserInput!): User!
deleteUser(id: ID!): User!
}
The DGS framework is designed for schema first development. The framework picks up any schema files in the src/main/resources/schema folder. We created schema file schema.graphql. In this file are defined types, queries and mutations. Regarding this class we create classes in specific packages in the application.
It is possible using DGS plugin to generated Java source code using previously defined GraphQL schemas. However, I prefer to use Lombok annotations, so I did that manually.
Application Structure
This is application structure:
Implementation
The most important classes are:
Domain
(/graphql-spring-boot/src/main/java/rs/karajovic/milan/graphql_spring_boot/domain
):
- Thera are defined domain object which exists in the above mentioned file src/main/resources/schema.
Repository
(/graphql-spring-boot/src/main/java/rs/karajovic/milan/graphql_spring_boot/repository
):
- Thera are defined JPA repository which is used from the application to communicate with databse.
Service
(/graphql-spring-boot/src/main/java/rs/karajovic/milan/graphql_spring_boot/service
):
- Service is layer which is between fatcher and repository.
Fetcher
(/graphql-spring-boot/src/main/java/rs/karajovic/milan/graphql_spring_boot/fetcher
):
(This is central part of the DGS GraphQL application. There are defined Queries and Mutations regarding to the declaration in the schema.graphql file).
- Netflix DGS provides annotation-based support for Spring Boot.
- The
UserFetcher
is responsible for defining queries related to theUser
object. We should annotate such a class with@DgsComponent
. Then, we have to annotate every query method with@DgsData
. The fieldsparentType
and fields should match the names declared in GraphQL schemas for Queries. We can see that in the fileschema.graphql
are defined two queries, so we have two methods insideUserFetcher
. To fetch data from the database are used methodes from theService
class. The last query methodfindUserById
performs advanced filtering based on the user id field passed in the input. To pass an input parameter we should annotate the method argument with@InputArgument
.
package rs.karajovic.milan.graphql_spring_boot.fetcher;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import com.netflix.graphql.dgs.DgsComponent;
import com.netflix.graphql.dgs.DgsData;
import com.netflix.graphql.dgs.InputArgument;
import rs.karajovic.milan.graphql_spring_boot.domain.User;
import rs.karajovic.milan.graphql_spring_boot.service.UserService;
/**
*
* @author Milan Karajovic <[email protected]>
*
*/
@DgsComponent
public class UserFetcher {
@Autowired
UserService userService;
@DgsData(parentType = "Query", field = "users")
public List<User> findAllUsers() {
return userService.findAll();
}
@DgsData(parentType = "Query", field = "user")
public User findUserById(@InputArgument("id") Integer id) {
return userService.findUserById(id);
}
}
- In comparison to the
UserFetcher
,UserMutation
implementation is similar. The fieldsparentType
and fields should match the names declared in GraphQL schemas for Mutations. To pass an input parameter we should annotate the method argument with@InputArgument
. To execute Muttaion methodes are used methodes from theService
class.
package rs.karajovic.milan.graphql_spring_boot.fetcher;
import org.springframework.beans.factory.annotation.Autowired;
import com.netflix.graphql.dgs.DgsComponent;
import com.netflix.graphql.dgs.DgsData;
import com.netflix.graphql.dgs.InputArgument;
import jakarta.validation.Valid;
import rs.karajovic.milan.graphql_spring_boot.domain.CreateUserInput;
import rs.karajovic.milan.graphql_spring_boot.domain.UpdateUserInput;
import rs.karajovic.milan.graphql_spring_boot.domain.User;
import rs.karajovic.milan.graphql_spring_boot.service.UserService;
/**
*
* @author Milan Karajovic <[email protected]>
*
*/
@DgsComponent
public class UserMutation {
@Autowired
UserService userService;
@DgsData(parentType = "Mutation", field = "createUser")
public User createUser(@InputArgument("input") CreateUserInput userInput) {
return userService.save(new User(null, userInput.getName(), userInput.getEmail()));
}
@DgsData(parentType = "Mutation", field = "updateUser")
public User updateUser(@InputArgument("id") Integer id, @Valid @InputArgument("input") UpdateUserInput userInput) {
return userService.updateUser(id, userInput.getName(), userInput.getEmail());
}
@DgsData(parentType = "Mutation", field = "deleteUser")
public User deleteUser(@InputArgument("id") Integer id) {
return userService.deleteUserById(id);
}
}
Database
- For this demo application is used inmemory H2 database. Configuration for the database is in the
application.properties
file. Thedata.sql
script is used for initial filing database with data.
Tests
- Tests are in the /graphql-spring-boot/src/test/java/rs/karajovic/milan/graphql_spring_boot/fetcher . There are tests for the Queries and Mutations.
Run Application in Your Development Environment
Run Spring Boot application in your development environment:
- Access to the H2 database:
http://localhost:8080/h2-console
- Access to the GraphiQL dashboard:
http://localhost:8080/graphiql
Build, and Run Application Using Docker
- Prerequisite is to have installed Docker on your machine.
- Download source code for this exaple from the GitHub.
-
Build the application using Maven (For building, I suggest to use STS or intelliJ IDEA development environment. However, you can do that also manually with maven command).
mvn clean install
- The .jar file is created in the folder:
/graphql-spring-boot/target/graphql-spring-boot-0.0.1-SNAPSHOT.jar
- Using Docker file, we create image
graphql-spring-boot
using next command in the console (It is necessary to open the console in the root folder of the project). The command is:
docker image build -t graphql-spring-boot .
- Now, we can start applicaton using docker-compose.yaml file. (It is necessary to open the console in the root folder of the project). The command is:
docker-compose up
- Now we can access to the GraphiQL dashboard with:
http://localhost/graphiql
- After application is success started, let’s just use the GraphiQL tool to run test queries. It is automatically included in the application by the Netflix DGS library. We may display it by invoking the URL
GraphiQL Dashboard
- GraphQL tool dashboard. There are all Queries and Mutations:
- Show all users in database using Query:
- Create user using Mutation:
- Show all users after created new user in the previous step:
- Delete user by ID:
You can check all Queries and Mutation yourself using this dashboard.
Contact and Support
- Author: Milan Karajovic
- GitHub: https://github.com/Milan-Karajovic/GraphQL-SpringBoot
- LinkedIn: https://www.linkedin.com/in/milan-karajovic-3a1243359/
Opinions expressed by DZone contributors are their own.
Comments