<  Back to Blogs

Integrating GraphQL with a Relational Database using Spring Data JPA

This blog explores integrating GraphQL and Spring Data JPA to create flexible, efficient web applications. It covers GraphQL's query language, Spring Data JPA’s data access, and compares REST with GraphQL. A practical guide demonstrates setting up a Spring Boot project, creating entities, repositories, and GraphQL schema, and implementing queries and mutations to efficiently fetch and manipulate data.

Anubhav Nath

Software Engineer

November 14, 2024

 Introduction

In the ever-evolving landscape of web development, efficient data fetching and manipulation have become crucial. Two technologies that have gained significant traction in this domain are GraphQL and Spring Data JPA. In this comprehensive blog, we'll together explore how these technologies can work together to create powerful, flexible, and efficient web applications.

1. Understanding GraphQL

1.1 What is GraphQL?

        

GraphQL is a query language and runtime for APIs that was developed by Facebook in 2012 and open-sourced in 2015. Unlike traditional REST APIs, GraphQL allows clients to request exactly the data they need, no more and no less. This flexibility can lead to more efficient data fetching and reduced over-fetching or under-fetching of data.

 1.2 Key Concepts in GraphQL

  1. Schema  : Defines the structure of your data and the operations that can be performed.
  2. Types        : Describe the shape of your data. The most common types are Object

                     types,  which represent a kind of object you can fetch from your service.

  1. Queries     :  Allow clients to fetch data.
  2.  Mutations : Enable clients to modify data.
  3.  Resolvers : Functions that resolve the data for each field in your schema.

1.3 Advantages of GraphQL

  • Flexible Data Fetching : Clients can request exactly what they need.
  • Strong Typing : The schema provides a clear contract between client and server.
  • Single Endpoint : All data can be accessed through a single API endpoint.
  • Real-time Updates : GraphQL subscriptions allow for real-time data updates.

2. Spring Data JPA: Simplifying Data Access

2.1 What is Spring Data JPA?

Spring Data JPA is part of the larger Spring Data project and aims to significantly improve the implementation of data access layers by reducing the effort to the amount that's actually needed. It provides a more elegant and efficient way to interact with databases in Spring applications.

2.2 Key Concepts in Spring Data JPA

1. Repositories : Interfaces that provide CRUD operations for a specific type.

2. Entities : Java classes that represent tables in your database.

3. Query Methods : Methods in repositories that are automatically implemented based on 

    their name.

4. Pagination and Sorting : Built-in support for pagination and sorting query results.

2.3 Advantages of Spring Data JPA

  • Reduced Boilerplate Code : Automatically implements basic CRUD operations.
  • Consistent Data Access APIs : Provides a consistent programming model across different data stores.
  • Integration with Spring Framework : Seamlessly integrates with other Spring technologies.
  • Database Independence : Allows switching between different database vendors with 

            minimal code changes.

3. REST vs GraphQL: A Comparative Analysis

To provide a clear overview of the differences between REST and GraphQL, let's examine them side-by-side in the following comparison table:

REST and GraphQL represent two distinct approaches to building APIs, each with its own strengths and use cases. REST, the traditional approach, uses multiple endpoints and fixed data structures, making it simple to understand and implement but potentially leading to over-fetching or under-fetching of data.

GraphQL, on the other hand, uses a single endpoint and allows clients to request exactly the data they need, offering greater flexibility and efficiency in data fetching. While REST leverages standard HTTP caching and has a mature ecosystem, GraphQL provides powerful introspection capabilities and excels in scenarios with complex, interconnected data. REST is often preferred for simple APIs and when HTTP caching is crucial, while GraphQL shines in applications with diverse client requirements and rapidly evolving data needs. 

Ultimately, the choice between REST and GraphQL depends on the specific requirements of your project, and many modern applications successfully utilise both technologies where appropriate.

4. Integrating GraphQL and Spring Data JPA

Now that we understand the basics of both GraphQL and Spring Data JPA, let's explore how we can integrate them to build a powerful API.

4.1 The Synergy

GraphQL's flexible querying capabilities pair exceptionally well with Spring Data JPA's efficient data access methods. Here's why:

1. Efficient Data Fetching : GraphQL allows clients to request only the data they need, while Spring Data JPA efficiently retrieves this data from the database.

2. Type Safety : Both technologies provide strong typing, ensuring data integrity from the database to the client.

3.Flexibility : GraphQL's schema can easily map to JPA entities, allowing for a natural representation of your data model.

4. Scalability : As your application grows, both GraphQL and Spring Data JPA provide mechanisms to handle complex queries and relationships efficiently.

5. Implementation Guide

Now, let's walk through the process of implementing a GraphQL API with Spring Boot and Spring Data JPA.

5.1 Setting Up the Project

First, we'll set up a new Spring Boot project. You can use Spring Initializr (https://start.spring.io/) to bootstrap your project. Include the following dependencies:

- Spring Web

- Spring Data JPA

- H2 Database (for simplicity, we'll use an in-memory database)

- GraphQL Spring Boot Starter

Once you have your project set up, let's dive into the implementation.

5.2 Defining the Entity

We'll start by creating a simple entity that represents a book in our database. This class will be mapped to a table in our database.

package com.example.personal_finance_manager.entity;

import jakarta.persistence.*;

import lombok.AllArgsConstructor;

import lombok.Data;

import lombok.NoArgsConstructor;

@Entity

@Data

@NoArgsConstructor

@AllArgsConstructor

@Table(name = "books")

public class Book {

   @Id

   @GeneratedValue(strategy = GenerationType.IDENTITY)

   private Long id;

   @Column(nullable = false)

   private String title;

   @Column(nullable = false)

   private String author;

   @Column(length = 1000)

   private String description;

   @Column(name = "publication_year")

   private Integer publicationYear;

}

This `Book` entity represents a book with an ID, title, author, description, and publication year. The `@Entity` annotation marks this class as a JPA entity, and the `@Table` annotation specifies the name of the database table.

5.3 Creating the Repository

Next, we'll create a repository interface that extends JpaRepository. This interface will provide us with basic CRUD operations and more.

package com.example.personal_finance_manager.repository;

import com.example.personal_finance_manager.entity.Book;

import org.springframework.data.jpa.repository.JpaRepository;

import org.springframework.stereotype.Repository;

import java.util.List;

@Repository

public interface BookRepository extends JpaRepository<Book, Long> {

   List<Book> findByAuthor(String author);

}

By extending `JpaRepository`, we get methods like `save()`, `findById()`, `findAll()`, `delete()`, and more, all without writing any implementation code.

5.4 Defining the GraphQL Schema

Now, let's define our GraphQL schema. Create a file named `schema.graphqls` in the `src/main/resources/graphql` directory.

type Book {

id: ID!

title: String!

author: String!

description: String

publicationYear: Int

}

type Query {

allBooks: [Book!]!

book(id: ID!): Book

booksByAuthor(author: String!): [Book!]!

}

type Mutation {

createBook(title: String!, author: String!, description: String, publicationYear: Int): Book!

updateBook(id: ID!, title: String, author: String, description: String, publicationYear: Int): Book

deleteBook(id: ID!): Boolean

}

This schema defines:

- A `Book` type that matches our JPA entity.

- Queries to fetch all books, a single book by ID, and books by author.

- Mutations to create, update, and delete books.

5.5 Implementing the Resolver

Now, let's create a resolver class to handle GraphQL queries and mutations.

package com.example.personal_finance_manager.controller;

import com.example.personal_finance_manager.entity.Book;

import com.example.personal_finance_manager.repository.BookRepository;

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 BookRepository bookRepository;

   public BookController(BookRepository bookRepository) {

       this.bookRepository = bookRepository;

   }

   @QueryMapping

   public List<Book> allBooks() {

       return bookRepository.findAll();

   }

   @QueryMapping

   public Book book(@Argument Long id) {

       return bookRepository.findById(id).orElse(null);

   }

   @QueryMapping

   public List<Book> booksByAuthor(@Argument String author) {

       return bookRepository.findByAuthor(author);

   }

   @MutationMapping

   public Book createBook(@Argument String title, @Argument String author,

                          @Argument String description, @Argument Integer publicationYear) {

       Book book = new Book();

       book.setTitle(title);

       book.setAuthor(author);

       book.setDescription(description);

       book.setPublicationYear(publicationYear);

       return bookRepository.save(book);

   }

   @MutationMapping

   public Book updateBook(@Argument Long id, @Argument String title, @Argument String author,

                          @Argument String description, @Argument Integer publicationYear) {

       Book book = bookRepository.findById(id).orElseThrow(() -> new RuntimeException("Book not found"));

       if (title != null) book.setTitle(title);

       if (author != null) book.setAuthor(author);

       if (description != null) book.setDescription(description);

       if (publicationYear != null) book.setPublicationYear(publicationYear);

       return bookRepository.save(book);

   }

   @MutationMapping

   public boolean deleteBook(@Argument Long id) {

       bookRepository.deleteById(id);

       return true;

   }

}

This resolver class uses Spring GraphQL's annotations to map GraphQL operations to Java methods. It interacts with the `BookRepository` to perform database operations.

5.6 Configuring the Application

Finally, let's configure our application to use GraphQL. Add the following properties to your `application.properties` file:

spring.graphql.graphiql.enabled=true

spring.graphql.graphiql.path=/graphiql

spring.datasource.url=jdbc:h2:mem:testdb

spring.datasource.driverClassName=org.h2.Driver

spring.datasource.username=sa

spring.datasource.password=password

spring.jpa.database-platform=org.hibernate.dialect.H2Dialect

spring.jpa.hibernate.ddl-auto=update

These properties:

- Enable the GraphiQL interface for testing our API

- Configure the H2 in-memory database

- Set up JPA to automatically update the database schema

6. Testing the API

Now that we have everything set up, run your Spring Boot application. You can access the GraphiQL interface at `http://localhost:8080/graphiql`.

Try out these GraphQL queries and mutations:

1. Create a new book:

mutation {

   createBook(

         title: "1984"

   author: "George Orwell"

   description: "A dystopian novel set in a totalitarian society"

   publicationYear: 1949

 ) {

      id

            title

      author

            description

      publicationYear

   }

}

2. Update any book

mutation {

   updateBook(

         id: 1

   title: "Nineteen Eighty-Four"

   description: "A classic dystopian novel exploring themes of totalitarianism and surveillance"

 ) {

      id

            title

      author

            description

      publicationYear

   }

}

3. Delete any book :

mutation {

   deleteBook(id: 1)

}

4. Fetch all books : 

query {

   allBooks {

      id

            title

      author

            description

      publicationYear

   }

}

5. Fetch a single book by ID:

mutation {

   deleteBook(id: 1)

}

Conclusion

In this comprehensive guide, we've explored the theoretical foundations of GraphQL and Spring Data JPA, and how they can be integrated to create a powerful, flexible API. I've walked you through the process of setting up a project, defining entities and repositories, creating a GraphQL schema, implementing resolvers, and even touched on advanced concepts like pagination, sorting, and error handling.

The combination of GraphQL's flexible querying capabilities with Spring Data JPA's efficient data access methods provides a robust foundation for building modern web applications. This approach allows for a clear separation of concerns, type-safe queries, and efficient data fetching, all while leveraging the power and simplicity of the Spring ecosystem.

Happy coding !!

address
205, 2nd floor, JSA Towers, Paramahansa Yogananda Rd, Indira Nagar 1st Stage, Hoysala Nagar, Indiranagar, Bengaluru, Karnataka 560038
© 2019 All Rights Reserved
© 2019 All Rights Reserved