Spring for GraphQL
Getting started with GraphQL with Spring boot
1. Introduction:-
GraphQL is a relatively new concept from Facebook, billed as an alternative to REST for Web APIs.
In this tutorial, we’ll learn how to set up a GraphQL server using Spring Boot so that we can add it to existing applications or use it in new ones.
2. What is GraphQL?
In the world of web development, efficient data retrieval and manipulation are crucial for creating powerful and responsive applications. Traditionally, RESTful APIs have been the go-to solution for interacting with servers and fetching data. However, as applications grow more complex and data requirements become increasingly diverse, a new approach called GraphQL has emerged to address these challenges.
GraphQL, first introduced by Facebook in 2015, is an open-source query language and runtime for APIs. Unlike REST, which relies on multiple endpoints to fetch specific data, GraphQL offers a more flexible and efficient way to retrieve and manipulate data from servers. It provides a standardized, declarative syntax for querying and mutating data, making it easier for developers to specify exactly what they need and receive precisely that.
At its core, GraphQL is designed to empower clients by giving them control over the data they receive. Instead of relying on predefined structures and fixed responses, GraphQL allows clients to request specific fields and their relationships, reducing over-fetching and under-fetching of data. This level of control results in more efficient data transfers, improved performance, and a better user experience.
One of the key features of GraphQL is its ability to aggregate data from multiple sources into a single request. With traditional REST APIs, clients often need to make several requests to different endpoints to gather all the required data. In contrast, GraphQL enables clients to retrieve related data in a single query, optimizing network round trips and minimizing latency.
Another advantage of GraphQL is its introspective nature. By utilizing the GraphQL schema, clients can easily explore the available data and operations supported by the server. This introspection capability simplifies API discovery and documentation, making it easier for developers to understand and utilize the API without relying on external documentation.
Additionally, GraphQL is programming language-agnostic, meaning it can be used with any programming language or framework. This flexibility has contributed to its growing adoption across various tech stacks and platforms.
2.1 GraphQL Schema
In GraphQL, a schema is a central component that defines the structure, capabilities, and relationships of your API. It acts as a contract between the client and the server, outlining the types of data that can be queried or mutated and the operations that can be performed. The schema serves as a blueprint for your API, providing a clear and standardized way to interact with the underlying data.
A GraphQL schema is defined using the GraphQL Schema Definition Language (SDL), which provides a concise and human-readable syntax. The SDL allows you to describe object types, fields, relationships, enumerations, and mutations.
type Author {
id: ID!
name: String!
books: [Book]
}
type Book {
id: ID!
title: String!
author: Author!
publicationYear: Int!
}
type Query {
findById(id:ID!): Book!
findAll: [Book!]
}
type Mutation {
createBook(title: String!, authorId: ID!, publicationYear: Int!): Book!
createAuthor(name:String!): Author!
}
3. Getting started with Spring for GraphQL
3.1. To create a Spring Boot project using Gradle, you can follow these steps:
Step 1: Visit start.spring.io Open your web browser and go to the Spring Initializr website at https://start.spring.io.
Step 2: Configure project settings On the Spring Initializr page, you’ll see several input fields and options to configure your project. Here are some key settings:
- Project: Select “Gradle Project” as the project type.
- Language: Choose the appropriate language Java. You can select other languages also
- Spring Boot: Select the desired Spring Boot version.
- Group and Artifact: Specify the package and artifact names for your project.
- Dependencies: Add necessary dependencies for graphQL project, such as Spring Web, Spring for GraphQL , Spring Data JPA, etc. You can search and select dependencies from the provided list.
Step 3: Generate the project Once you’ve configured the project settings and dependencies, click on the “Generate” or “Generate Project” button.
Step 4: Download and extract the project After generating the project, a ZIP file containing the project structure and files will be downloaded to your computer. Extract the contents of the ZIP file to your desired project directory.
Step 4: Open project folder in your favourite IDE.
3.2. All the required dependencies added in build.gradle for this project
plugins {
id 'java'
id 'org.springframework.boot' version '3.0.5'
id 'io.spring.dependency-management' version '1.0.15.RELEASE'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
compileJava {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
repositories {
mavenLocal()
maven {
url = uri('https://repo.maven.apache.org/maven2/')
}
mavenCentral()
}
dependencies {
compileOnly 'org.projectlombok:lombok:1.18.20'
annotationProcessor 'org.projectlombok:lombok:1.18.20'
implementation 'com.h2database:h2:1.4.200'
implementation 'org.postgresql:postgresql:42.3.1'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-graphql'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.projectlombok:lombok:1.18.26'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework:spring-webflux'
testImplementation 'org.springframework.graphql:spring-graphql-test'
}
tasks.named('test') {
useJUnitPlatform()
}
The spring-graphql-test provides additional capabilities for building unit tests. The starter(spring-boot-starter-graphql) comes with required libraries and auto-configuration. However, it does not enable the GraphiQL interface(GraphiQL is the GraphQL integrated development environment). In order to enable it, we should set the following property in the application.yml
file:
spring:
graphql:
graphiql:
enabled: true
3.3. GraphQL Schema
By default, Spring for GraphQL tries to load schema files from the directory src/main/resources/graphql
. It looks there for the files with the following extensions {.graphqls
or .gqls
}.
Create Schema File
Let’s create graphQL schema for the Book entity. The Book type references the one other types : Author . There are two queries for searching all books and a book by id, and a single mutation for adding a new book.
demo.graphqls
type Author {
id: ID!
name: String!
books: [Book]
}
type Book {
id: ID!
title: String!
author: Author!
publicationYear: Int!
}
type Query {
findById(id:ID!): Book!
findAll: [Book!]
}
type Mutation {
createBook(title: String!, authorId: ID!, publicationYear: Int!): Book!
createAuthor(name:String!): Author!
}
Create Entities
I personally prefer Lombok in entity implementation. Here’s the Book entity corresponding to Book type defined in GrphQL schema file.
Book.java
@Entity
@Table(name = "books")
@Data
@EqualsAndHashCode(callSuper = false)
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "author_id")
private Author author;
private int publicationYear;
}
Author.java
@Entity
@Table(name = "authors")
@Data
@EqualsAndHashCode(callSuper = false)
public class Author {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToMany(mappedBy = "author", cascade = CascadeType.ALL)
private List<Book> books;
}
3.4. Using Spring for GraphQL with spring boot
Create Controller
Spring for GraphQL provides an annotation based programming model using well known @controller pattern . We can use Querydsl library also with Spring data JPA.
Let’s start with our first controller. We need to annotate Query method with @QueryMapping, and mutation methods with @MutationMapping. To pass an input parameter we should annotate the method with @Argument .
BookController.java
@Controller
public class BookController {
@Autowired
BookRepository bookRepository;
@Autowired
AuthorRepository authorRepository;
@QueryMapping
public Book findById(@Argument Integer id) {
return bookRepository.findById(id).orElseThrow();
}
@QueryMapping
public Iterable<Book> findAll() {
return bookRepository.findAll();
}
@MutationMapping
public Book createBook(@Argument("title") String title, @Argument("authorId") Integer authorId, @Argument("publicationYear") int publicationYear) {
Author author = authorRepository.findById(authorId).orElseThrow();
Book book = new Book();
book.setTitle(title);
book.setAuthor(author);
book.setPublicationYear(publicationYear);
return bookRepository.save(book);
}
}
AuthorController.java
@Controller
public class AuthorController {
@Autowired
AuthorRepository authorRepository;
@MutationMapping
public Author createAuthor(@Argument("name") String name) {
Author author=new Author();
author.setName(name);
return authorRepository.save(author);
}
}
Create Repository
I am using postgres database but for testing you can use in-memory h2 database and dependency for same is present in build.gradle file .
In-memory database
spring:
datasource:
url: jdbc:h2:mem:testdb
driverClassName: org.h2.Driver
username: sa
password:
jpa:
database-platform: org.hibernate.dialect.H2Dialect
hibernate:
ddl-auto: 'create-drop'
show-sql: true
Postgres database:-
spring:
datasource:
url: jdbc:postgresql://localhost:5432/graphqldemo
driverClassName: org.postgresql.Driver
username: postgres
password: admin
jpa:
database-platform: org.hibernate.dialect.PostgreSQLDialect
hibernate:
ddl-auto: 'create-drop'
show-sql: true
AuthorRepository.java
@GraphQlRepository(typeName = "author")
public interface AuthorRepository extends CrudRepository<Author,Integer> {
}
BookRepository.java
@GraphQlRepository(typeName = "book")
public interface BookRepository extends CrudRepository<Book,Integer> {
}
Main Class
GraphqlDemoApplication.java
@SpringBootApplication
@EnableJpaRepositories
public class GraphqlDemoApplication {
public static void main(String[] args) {
SpringApplication.run(GraphqlDemoApplication.class, args);
}
}
3.5. Build and Run application
Open a terminal or command prompt, navigate to the project directory, and run the following command to build the project:
./gradlew build
Once the build is successful, run the following command to start the Spring Boot application:
./gradlew bootRun
or you can run application using IDE also.
4. Test GraphQL API using GraphiQL playground
4.1. Open http://localhost:8080/graphiql . Following empty screen will get opened.
4.2. Create mutation/query in blank editor and provide request variable/headers in respective section.
Note:- User can visit document explorer and find all the type and query/mutation present.
Example: Create Author
mutation createAuthor($name:String!){
createAuthor(name:$name){
id
name
books{
id
title
publicationYear
}
}
}
//Place in variable section
{
"name": "Mukesh Chaubey"
}
Example: Create Book
mutation createBook($title:String!,$authorId:ID!,$publicationYear:Int!){
createBook(title:$title,authorId:$authorId,publicationYear:$publicationYear){
id
title
author{
id
name
}
publicationYear
}
}
//Place in Variable Section
{
"title": "Test Book",
"authorId": 1,
"publicationYear": 2023
}
Example: Fetch book by id
query findById($id:ID!){
findById(id:$id){
id
title
author{
id
name
}
publicationYear
}
}
//Place in Variable Section
{
"id": 1
}
Example: Fetch all book
query findAll {
findAll {
id
title
author{
id
name
}
publicationYear
}
}