GraphQL is an efficient, flexible, and powerful API query language that enables clients to request only the data they need. Unlike REST, which relies on multiple endpoints, GraphQL operates through a single endpoint, making data retrieval more efficient.
In this article, we will build a GraphQL CRUD API using Spring Boot and PostgreSQL. We will implement a simple Book management system with operations for creating, updating, retrieving, and deleting books.
Details about GraphQL
GraphQL is an API query language developed by Facebook that allows clients to define the structure of the response they want. Unlike REST, which requires multiple endpoints for different resources, GraphQL enables data retrieval from a single endpoint, reducing over-fetching and under-fetching issues.
Key Benefits of GraphQL:
- Efficient Data Fetching: Clients request only the necessary data.
- Single Endpoint: Unlike REST, which has multiple endpoints, GraphQL uses a single endpoint.
- Strongly Typed Schema: GraphQL relies on a schema to define data structure.
- Real-time Updates: Supports subscriptions for real-time data fetching.
GraphQL Performance Compared to REST
GraphQL provides significant performance improvements over REST, especially when dealing with complex relationships between entities. Unlike REST APIs, which can over-fetch or under-fetch data, GraphQL ensures clients retrieve exactly what they need. This reduces bandwidth usage and improves response times. However, GraphQL queries can be more computationally expensive on the server, requiring dynamically resolving nested relationships.
REST vs GraphQL Demo Request-Response Example
REST API Example
Request (GET request to fetch a book by ID)
GET /books/1 HTTP/1.1
Host: example.com
Accept: application/json
Response
{
"id": 1,
"title": "GraphQL with Spring Boot",
"author": "John Doe",
"price": 29.99,
"publisher": "TechBooks",
"publishedYear": 2024
}
The issue with the REST API
The client receives all book details, even if it only needs the title and author.
GraphQL API Example
{
findBookById(id: 1) {
title
author
}
}
Response
{
"data": {
"findBookById": {
"title": "GraphQL with Spring Boot",
"author": "John Doe"
}
}
}
Advantage
The client receives only the requested fields (title and author), reducing unnecessary data transfer.
GraphQL Terms
- Schema: Defines the structure of the API and available operations.
- Types: Represents data models such as Book, User, etc.
- Queries: Used to fetch data from the server.
- Mutations: Used to modify data (create, update, delete).
- Input Types: Used to pass structured data as arguments to mutations.
Discussion About Our Project
Our project is a simple book management system that allows users to perform CRUD operations via GraphQL. We will use Spring Boot as the backend framework and PostgreSQL as the database. The application will include:
- GraphQL schema definition.
- CRUD operations for books.
- Integration with PostgreSQL using Spring Data JPA.
- A controller to handle GraphQL queries and mutations.
- Service and repository layers for better separation of concerns.
Project Setup
1. Dependencies
To set up GraphQL with Spring Boot, include the following dependencies in your pom.xml
:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-graphql</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
2. Application Configurations
In application.yaml
, configure the PostgreSQL database:
spring:
application:
name: graphql-postgres-crud
datasource:
url: jdbc:postgresql://localhost:5432/bookdb
username: postgres
password: mysecretpassword
driver-class-name: org.postgresql.Driver
jpa:
hibernate:
ddl-auto: update
graphql:
graphiql:
enabled: true
cors:
allow-credentials: true
allowed-origins: http://localhost:5173
Details about the GraphQL-specific Properties
spring.graphql.graphiql.enabled
: Enables GraphiQL UI for testing queries.
spring.graphql.cors.allow-credentials
: Allows CORS credentials.
spring.graphql.cors.allowed-origins
: Defines allowed origins for GraphQL API.
Defining the GraphQL Schema
Create a GraphQL schema file at src/main/resources/graphql/schema.graphqls
:
type Book {
id: ID!
title: String!
author: String!
price: Float!
}
input BookInput {
title: String!
author: String!
price: Float!
}
type Query {
findAllBooks: [Book]!
findBookById(id: ID!): Book
}
type Mutation {
createBook(bookInput: BookInput!): Book!
updateBook(id: ID!, bookInput: BookInput!): Book!
deleteBook(id: ID!): Boolean!
}
Explanation about the schema.graphqls File
-
Types
: Defines the structure of the data (e.g., Book). -
Queries
: Handles fetching books from the database. -
Mutations
: Handles modifying book data. -
Input Types
: Used for mutation arguments.
Implementing the Model
Create the Book
entity:
@Entity
@AllArgsConstructor
@NoArgsConstructor
@Data
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
private String author;
private Double price;
}
Explanation about the Model
- Uses JPA annotations (
@Entity
,@Id
,@GeneratedValue
). - Uses Lombok annotations for boilerplate code reduction.
Creating the Repository
Create the BookRepository
interface:
public interface BookRepository extends JpaRepository<Book, Long> {}
Explanation of the Repository
- Extends
JpaRepository
to provide CRUD operations. Implementing the Service Layer
Implementing the Service Layer
The BookService
encapsulates business logic for interacting with the repository:
package com.mahmud.graphqlpostgrescrud.service;
import com.mahmud.graphqlpostgrescrud.model.Book;
import com.mahmud.graphqlpostgrescrud.repository.BookRepository;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class BookService {
private final BookRepository bookRepository;
public BookService(BookRepository bookRepository) {
this.bookRepository = bookRepository;
}
public List<Book> findAll() {
return bookRepository.findAll();
}
public Book findById(Long id) {
return bookRepository.findById(id).orElseThrow();
}
public Book save(Book book) {
return bookRepository.save(book);
}
public Book update(Long id, Book bookDetails) {
Book book = findById(id);
book.setTitle(bookDetails.getTitle());
book.setAuthor(bookDetails.getAuthor());
book.setPrice(bookDetails.getPrice());
return bookRepository.save(book);
}
public void deleteById(Long id) {
bookRepository.deleteById(id);
}
}
Explanation of the Services
- Contains business logic for handling books.
- Uses
BookRepository
for database interactions.
Building the GraphQL Controller
The BookController
maps GraphQL queries and mutations to service methods:
package com.mahmud.graphqlpostgrescrud.controller;
import com.mahmud.graphqlpostgrescrud.model.Book;
import com.mahmud.graphqlpostgrescrud.service.BookService;
import org.springframework.graphql.data.method.annotation.Argument;
import org.springframework.graphql.data.method.annotation.MutationMapping;
import org.springframework.graphql.data.method.annotation.QueryMapping;
import org.springframework.stereotype.Controller;
import java.util.List;
@Controller
public class BookController {
private final BookService bookService;
public BookController(BookService bookService) {
this.bookService = bookService;
}
@QueryMapping
public List<Book> findAllBooks() {
return bookService.findAll();
}
@QueryMapping
public Book findBookById(@Argument Long id) {
return bookService.findById(id);
}
@MutationMapping
public Book createBook(@Argument BookInput bookInput) {
Book book = new Book();
book.setTitle(bookInput.title());
book.setAuthor(bookInput.author());
book.setPrice(bookInput.price());
return bookService.save(book);
}
@MutationMapping
public Book updateBook(@Argument Long id, @Argument BookInput bookInput) {
Book book = new Book();
book.setTitle(bookInput.title());
book.setAuthor(bookInput.author());
book.setPrice(bookInput.price());
return bookService.update(id, book);
}
@MutationMapping
public Boolean deleteBook(@Argument Long id) {
bookService.deleteById(id);
return true;
}
public record BookInput(String title, String author, Double price) {}
}
Details about the Controller
- Uses
@Controller
to define GraphQL endpoints. - Uses
@QueryMapping
for queries. - Uses
@MutationMapping
for mutations. - Uses
@Argument
to extract input values.
Running the Application
Start the Spring Boot application:
mvn spring-boot:run
Testing GraphQL Queries and Mutations
Go to http://localhost:8080/graphiql
. You will see a GraphQL Client to test the API we have created.
Create a Book
mutation {
createBook(bookInput: {title: "GraphQL with Spring Boot", author: "John Doe", price: 29.99}) {
id
title
author
}
}
Query All Books
{
findAllBooks {
id
title
author
price
}
}
Query Book by ID
{
findBookById(id: 1) {
title
author
price
}
}
Update a Book
mutation {
updateBook(id: 1, bookInput: {title: "Updated Title", author: "Jane Doe", price: 35.99}) {
id
title
}
}
Delete a Book
mutation {
deleteBook(id: 1)
}
Conclusion
This article demonstrated how to build a GraphQL CRUD API with Spring Boot and PostgreSQL. GraphQL provides a flexible and efficient way to fetch data, reducing over-fetching and under-fetching issues commonly found in REST APIs. Happy coding!
Top comments (0)