📣 Requestly API Client – Free Forever & Open Source. A powerful alternative to Postman. Try now ->

Master GraphQL API Development with Spring Boot

Rashmi Saini

GraphQL with Spring Boot offers enhanced efficiency by enabling clients to request only the data they need through a single, flexible endpoint. It simplifi…

GraphQL with Spring Boot offers enhanced efficiency by enabling clients to request only the data they need through a single, flexible endpoint. It simplifies API development, improves performance, and ensures seamless schema evolution, making it ideal for building modern, scalable applications.

This article explores how to seamlessly integrate GraphQL with Spring Boot, guiding readers through setup, schema creation, CRUD methods, database integration, security, optimization, and deployment for building efficient APIs.

Understanding GraphQL with Spring Boot

GraphQL is a query language for APIs that allows clients to request exactly the data they need, improving efficiency and flexibility compared to traditional REST APIs.

Spring Boot, with its auto-configuration and extensive ecosystem, provides a powerful foundation for building GraphQL services quickly and effectively. It supports defining GraphQL schemas using SDL files, which are automatically detected and integrated at startup, simplifying schema management.

Spring Boot’s annotations like @QueryMapping and @MutationMapping make it easy to implement resolvers that handle client queries and mutations, while integration with Spring Data allows seamless database interactions.

Additionally, Spring Boot’s support for security, validation, and testing ensures that GraphQL APIs are robust and production-ready. This combination makes Spring Boot an excellent choice for developers looking to build scalable, maintainable, and performant GraphQL backends with minimal configuration and maximum productivity.


Requestly

Setting Up Spring Boot for GraphQL

Setting up a Spring Boot project to work with GraphQL is straightforward thanks to Spring Boot’s auto-configuration and starter dependencies. Follow these essential steps:

1. Create a Spring Boot Project

You can start by generating a Spring Boot project using Spring Initializr (https://start.spring.io) or your favorite IDE such as IntelliJ IDEA or Eclipse.

  • Choose Spring Web and Spring Boot Starter GraphQL dependencies.
  • Optionally, add Spring Data JPA and your preferred database driver (e.g., H2, PostgreSQL).

2. Add Dependencies in pom.xml

If you are using Maven, your pom.xml should include:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-graphql</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <scope>runtime</scope>
</dependency>

For Gradle, add similar dependencies in build.gradle.

3. Define GraphQL Schema Files

Create a folder src/main/resources/graphql/ and add your schema definition file, e.g., schema.graphqls:

type Query {
    books: [Book]
}
type Book {
    id: ID
    title: String
    author: String
}

Spring Boot automatically detects and loads this schema during application startup.

4. Configure Application Properties

Add the following properties to src/main/resources/application.properties to customize the GraphQL endpoint and other settings:

spring.graphql.path=/graphql
spring.graphql.schema.locations=classpath:graphql/

5. Implement Data Fetchers (Resolvers)

Create resolver classes using Spring annotations like @QueryMapping.

Example of a simple resolver to return a list of books:

import org.springframework.graphql.data.method.annotation.QueryMapping;
import org.springframework.stereotype.Controller;
import java.util.List;
@Controller
public class BookController {
    @QueryMapping
    public List<Book> books() {
        return List.of(
            new Book(1L, "1984", "George Orwell"),
            new Book(2L, "To Kill a Mockingbird", "Harper Lee")
        );
    }
}
Define the Book model:
public record Book(Long id, String title, String author) {}

6. Run the Application

Run the Spring Boot application. You can test the GraphQL API at the configured endpoint (default /graphql).

This setup provides a clean, modular foundation for building GraphQL APIs with Spring Boot, enabling fast development and easy customization as your application grows.

Creating the GraphQL Schema

Creating the GraphQL schema is a crucial step in defining the structure of your API, including the types of data clients can query or mutate and the relationships between those types. In Spring Boot, schemas are typically defined using the Schema Definition Language (SDL) in .graphqls files.

1. Define Schema in .graphqls File

Create your GraphQL schema file under src/main/resources/graphql/. For example, a file named schema.graphqls may look like this:

type Query {
    bookById(id: ID!): Book
    allBooks: [Book!]!
}
type Mutation {
    addBook(input: BookInput!): Book!
}
type Book {
    id: ID!
    title: String!
    author: String!
    publishedYear: Int
}
input BookInput {
    title: String!
    author: String!
    publishedYear: Int
}
  • Query type defines read operations.
  • Mutation type defines write operations such as adding or updating data.
  • Book is an object type describing the data model.
  • BookInput is an input type used for mutations to pass structured data.

2. File Location and Naming

  • Place schema files in src/main/resources/graphql/
  • Spring Boot automatically detects and loads all .graphqls schema files from this directory.

3. Schema Design Best Practices

  • Use non-null types (!) to enforce required fields.
  • Separate input types from output types to clearly distinguish between data sent to and received from the API.
  • Keep schemas modular by splitting large schemas into multiple .graphqls files if needed.

4. Schema Extension and Evolution

To evolve your schema without breaking clients, use techniques like deprecating fields:

type Book {
    id: ID!
    title: String!
    author: String!
    publishedYear: Int
    summary: String @deprecated(reason: "Use description field instead")
    description: String
}

This enables smooth backward compatibility while adding new features.

Methods to Implement the CRUD Operations

Implementing CRUD (Create, Read, Update, Delete) operations in GraphQL with Spring Boot involves defining resolver methods that correspond to each operation. These methods handle data interactions, typically connecting to a database via repositories or services.

1. Create Operation (Mutations)

Create mutations to add new data:

@MutationMapping
public Book addBook(@Argument BookInput input) {
    Book newBook = new Book(null, input.getTitle(), input.getAuthor(), input.getPublishedYear());
    return bookRepository.save(newBook);
}

This method takes structured input, creates a new entity, and persists it.

2. Read Operations (Queries)

Read methods to fetch data:

@QueryMapping
public List<Book> allBooks() {
    return bookRepository.findAll();
}
@QueryMapping
public Book bookById(@Argument Long id) {
    return bookRepository.findById(id).orElse(null);
}

These fetch all records or a specific record by ID.

3. Update Operation

Update mutations modify existing data:

@MutationMapping
public Book updateBook(@Argument Long id, @Argument BookInput input) {
    return bookRepository.findById(id)
        .map(book -> {
            book.setTitle(input.getTitle());
            book.setAuthor(input.getAuthor());
            book.setPublishedYear(input.getPublishedYear());
            return bookRepository.save(book);
        }).orElse(null);
}

It retrieves the entity, updates fields, and saves.

4. Delete Operation

Define delete mutations to remove data:

@MutationMapping
public Boolean deleteBook(@Argument Long id) {
    if (bookRepository.existsById(id)) {
        bookRepository.deleteById(id);
        return true;
    }
    return false;
}

Returns success status based on the deletion outcome.

How to Integrate the Databases with Spring Boot and GraphQL

Integrating databases with your Spring Boot GraphQL application involves connecting your data layer to the resolver methods, enabling seamless data retrieval and modification. The most common approach is using Spring Data JPA, but other options like JDBC Template or MyBatis can also be used.

1. Add Dependencies

Include dependencies for your preferred database and Spring Data JPA in your pom.xml:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <scope>runtime</scope>
</dependency>

Replace H2 with your database driver (PostgreSQL, MySQL, etc.) as needed.

2. Configure Application Properties

Set database connection details in application.properties:

spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.jpa.hibernate.ddl-auto=update

Modify these settings for your database server (URL, username, password).

3. Define Entity Classes

Create JPA entities that mirror your database tables:

import javax.persistence.*;
@Entity
public class Book {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String title;
    private String author;
    private Integer publishedYear;
    // getters/setters
}

4. Create Repository Interfaces

Define repositories to perform CRUD operations:

import org.springframework.data.jpa.repository.JpaRepository;
public interface BookRepository extends JpaRepository<Book, Long> {
}

Spring Data JPA provides implementation at runtime.

5. Use Repositories in Resolvers

Inject repositories in your resolver classes:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.graphql.data.method.annotation.QueryMapping;
import org.springframework.stereotype.Controller;
@Controller
public class BookResolver {
    @Autowired
    private BookRepository bookRepository;
    @QueryMapping
    public List<Book> allBooks() {
        return bookRepository.findAll();
    }
}

6. Run and Test

Start your application, and your GraphQL resolvers will now interact with the database seamlessly, allowing full CRUD operations.

Testing the GraphQL API

Testing is a critical phase in developing a GraphQL API to ensure the correctness, reliability, and performance of queries and mutations. Spring Boot offers several tools and approaches to effectively test your GraphQL endpoints.

1. Use WebGraphQlTester for Integration Testing

Spring Boot’s WebGraphQlTester allows you to write integration tests that simulate client requests against the GraphQL endpoint.

Example test for querying all books:

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.graphql.GraphQlTest;
import org.springframework.graphql.test.tester.WebGraphQlTester;
@GraphQlTest(BookController.class)
public class BookControllerTest {
    @Autowired
    private WebGraphQlTester graphQlTester;
    @Test
    void testAllBooksQuery() {
        String query = "{ allBooks { id title author } }";
        graphQlTester.document(query)
                     .execute()
                     .path("allBooks")
                     .entityList(Book.class)
                     .hasSizeGreaterThan(0);
    }
}

2. Mock Dependencies for Unit Testing

Use mock frameworks like Mockito to mock service and repository dependencies, isolating the resolver logic.

import static org.mockito.Mockito.*;
import org.junit.jupiter.api.BeforeEach;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
public class BookResolverUnitTest {
    @Mock
    private BookRepository bookRepository;
    @InjectMocks
    private BookController bookController;
    @BeforeEach
    public void setup() {
        MockitoAnnotations.openMocks(this);
    }
    // Write unit tests where repository behavior is stubbed
}

3. Validate Mutations and Input Handling

Write tests that verify mutations, handle input correctly and produce expected database changes or errors.

4. Use GraphiQL or Altair for Manual Testing

For exploratory and manual testing, use tools like GraphiQL or Altair GraphQL Client to execute queries and inspect responses interactively.

How to Secure GraphQL API

Securing your GraphQL API is essential to protect sensitive data and ensure reliable service. Key security practices include:

  • Authentication and Authorization: Implement strong authentication using standards like JWT or OAuth 2.0 to verify user identity. Follow this with fine-grained authorization to control access to specific queries, mutations, and fields based on user roles or permissions.
  • Transport Layer Security: Always use HTTPS to encrypt data transmitted between clients and the server, preventing interception and tampering.
  • Limit Query Complexity: Restrict the maximum depth and complexity of queries to avoid denial-of-service attacks caused by overly expensive or deeply nested queries.
  • Input Validation and Sanitization: Validate all incoming data rigorously to prevent injection attacks and other malicious payloads, using GraphQL’s built-in scalar types, enums, and custom validators as needed.
  • Disable Introspection and Schema Suggestions in Production: This reduces the attack surface by preventing external users from exploring the API schema and discovering internal details.
  • Rate Limiting: Apply limits on the number of requests per client IP or user to mitigate brute force and abuse attempts.
  • Effective Error Handling and Logging: Ensure errors do not expose sensitive internal information. Maintain logs for auditing but avoid logging sensitive data.
  • Regular Security Audits and Monitoring: Conduct periodic vulnerability assessments and monitor live traffic to detect suspicious activities promptly.

Implementing these layered defenses creates a robust security posture, safeguarding your GraphQL API from common threats while preserving functionality and performance.

Tips to Optimize Performance

Optimizing the performance of a GraphQL API involves several key strategies to ensure fast, efficient, and scalable data delivery.

  • Minimize Query Size: Limit queries to request only the necessary fields to reduce payload size and bandwidth consumption. Avoid overfetching by designing precise, focused queries.
  • Implement Pagination: Use pagination to break large result sets into smaller, manageable chunks. Cursor-based pagination is preferable for large datasets, while offset pagination suits smaller ones.
  • Batch and Cache Data Fetching: Use tools like DataLoader to batch multiple database requests into a single query and cache results, mitigating the N+1 query problem and reducing database load.
  • Limit Query Complexity and Depth: Define constraints on the maximum query depth and overall complexity to prevent expensive queries from overwhelming server resources.
  • Use HTTP GET for Queries: Employ GET requests for queries to leverage HTTP caching mechanisms and Content Delivery Networks (CDNs), enhancing response times.
  • Compress Responses: Enable GZIP compression on API responses to reduce data transfer size between server and client.
  • Monitor and Profile Performance: Regularly monitor query execution times, log bottlenecks, and use profiling tools to identify and optimize slow resolvers.
  • Design Efficient Schema: Avoid overly complex schema designs, keep it modular, and prioritize schema patterns that enable efficient querying and avoid redundant data fetching.

Applying these techniques leads to improved responsiveness, scalability, and overall user experience for your GraphQL API.

Deploying the Application

Deploying a GraphQL Spring Boot application involves preparing and running your API efficiently in production environments. A typical deployment process includes the following steps:

1. Build the Executable

Build your Spring Boot application as a runnable fat JAR that bundles all dependencies:

./mvnw clean package
This creates a JAR file under target/ that you can run with:
java -jar target/your-app-name.jar

2. Externalize Configuration

Use application.properties or environment variables for production settings like database URLs and credentials:

spring.datasource.url=jdbc:mysql://prod-db-host:3306/mydb
spring.datasource.username=prod_user
spring.datasource.password=prod_password
spring.profiles.active=prod

Set environment variables on your server or container orchestration platform to override defaults.

4. Integrate Monitoring and Logging

Add tools like Prometheus for metrics and ELK stack or Grafana Loki for logs to monitor app health and diagnose issues in production.

5. Use Load Balancers and API Gateways

Place your app behind a load balancer or API gateway to balance requests, enable SSL termination, and apply rate-limiting or authentication policies.

6. Automate Deployment with CI/CD

Implement automated pipelines using Jenkins, GitHub Actions, or GitLab CI to build, test, and deploy your app reliably with minimal downtime.

7. Gradual Rollouts

Use blue-green or canary deployment strategies to roll out new versions safely, reducing user impact and enabling rollback if needed.

Enhance API Testing and Mocking with Requestly

Requestly is a powerful and versatile API client designed to supercharge your GraphQL and REST API testing workflows. It combines capabilities of traditional API testing tools with advanced request interception and modification features, enabling developers to debug, mock, and manipulate API requests directly within the browser or using a standalone app.

Key benefits include:

  • Request Interception and Modification: Easily intercept outgoing API requests to modify headers, query parameters, or payloads on the fly, without changing backend code.
  • Mock API Responses: Simulate real server responses instantly to unblock frontend development when backend APIs are incomplete or unstable.
  • Detailed Request Logging and Replay: Track API calls with logs and replay them for efficient repeated testing.
  • Environment Management and Team Collaboration: Manage variables and secrets securely, organize API collections, and collaborate using shared workspaces.
  • Lightweight and Privacy-Friendly: Runs as a Chrome or Firefox extension emphasizing local-first testing, keeping sensitive information safe on your device.

Conclusion

Building a GraphQL API with Spring Boot delivers a highly flexible and efficient backend solution that caters to dynamic client data needs. By focusing on clear schema design, robust CRUD operations, seamless database integration, thorough testing, and strong security measures, the API becomes both scalable and secure.

Optimizing performance through techniques like batching and pagination, along with strategic deployment practices, ensures reliability and responsiveness in real-world applications. Incorporating tools like Requestly further streamlines API development and testing, boosting productivity and confidence in delivering high-quality services. This holistic approach empowers developers to create maintainable, performant, and secure GraphQL APIs suited for modern application demands.

Written by
Rashmi Saini