DEV Community

Cover image for Building a GraphQL CRUD API with Spring Boot and PostgreSQL
M. Oly Mahmud
M. Oly Mahmud

Posted on

Building a GraphQL CRUD API with Spring Boot and PostgreSQL

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
Enter fullscreen mode Exit fullscreen mode

Response

{
  "id": 1,
  "title": "GraphQL with Spring Boot",
  "author": "John Doe",
  "price": 29.99,
  "publisher": "TechBooks",
  "publishedYear": 2024
}
Enter fullscreen mode Exit fullscreen mode

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
  }
}
Enter fullscreen mode Exit fullscreen mode

Response

{
  "data": {
    "findBookById": {
      "title": "GraphQL with Spring Boot",
      "author": "John Doe"
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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!
}

Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

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> {}
Enter fullscreen mode Exit fullscreen mode

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);
    }
}
Enter fullscreen mode Exit fullscreen mode

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) {}
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Testing GraphQL Queries and Mutations

Go to http://localhost:8080/graphiql. You will see a GraphQL Client to test the API we have created.

GraphQL Client

Create a Book

mutation {
  createBook(bookInput: {title: "GraphQL with Spring Boot", author: "John Doe", price: 29.99}) {
    id
    title
    author
  }
}
Enter fullscreen mode Exit fullscreen mode

Query All Books

{
  findAllBooks {
    id
    title
    author
    price
  }
}
Enter fullscreen mode Exit fullscreen mode

Query Book by ID

{
  findBookById(id: 1) {
    title
    author
    price
  }
}
Enter fullscreen mode Exit fullscreen mode

Update a Book

mutation {
  updateBook(id: 1, bookInput: {title: "Updated Title", author: "Jane Doe", price: 35.99}) {
    id
    title
  }
}
Enter fullscreen mode Exit fullscreen mode

Delete a Book

mutation {
  deleteBook(id: 1)
}
Enter fullscreen mode Exit fullscreen mode

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)