eBook – Guide Spring Cloud – NPI EA (cat=Spring Cloud)
announcement - icon

Let's get started with a Microservice Architecture with Spring Cloud:

>> Join Pro and download the eBook

eBook – Mockito – NPI EA (tag = Mockito)
announcement - icon

Mocking is an essential part of unit testing, and the Mockito library makes it easy to write clean and intuitive unit tests for your Java code.

Get started with mocking and improve your application tests using our Mockito guide:

Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Reactive – NPI EA (cat=Reactive)
announcement - icon

Spring 5 added support for reactive programming with the Spring WebFlux module, which has been improved upon ever since. Get started with the Reactor project basics and reactive programming in Spring Boot:

>> Join Pro and download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Jackson – NPI EA (cat=Jackson)
announcement - icon

Do JSON right with Jackson

Download the E-book

eBook – HTTP Client – NPI EA (cat=Http Client-Side)
announcement - icon

Get the most out of the Apache HTTP Client

Download the E-book

eBook – Maven – NPI EA (cat = Maven)
announcement - icon

Get Started with Apache Maven:

Download the E-book

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

eBook – RwS – NPI EA (cat=Spring MVC)
announcement - icon

Building a REST API with Spring?

Download the E-book

Course – LS – NPI EA (cat=Jackson)
announcement - icon

Get started with Spring and Spring Boot, through the Learn Spring course:

>> LEARN SPRING
Course – RWSB – NPI EA (cat=REST)
announcement - icon

Explore Spring Boot 3 and Spring 6 in-depth through building a full REST API with the framework:

>> The New “REST With Spring Boot”

Course – LSS – NPI EA (cat=Spring Security)
announcement - icon

Yes, Spring Security can be complex, from the more advanced functionality within the Core to the deep OAuth support in the framework.

I built the security material as two full courses - Core and OAuth, to get practical with these more complex scenarios. We explore when and how to use each feature and code through it on the backing project.

You can explore the course here:

>> Learn Spring Security

Course – LSD – NPI EA (tag=Spring Data JPA)
announcement - icon

Spring Data JPA is a great way to handle the complexity of JPA with the powerful simplicity of Spring Boot.

Get started with Spring Data JPA through the guided reference course:

>> CHECK OUT THE COURSE

Partner – Moderne – NPI EA (cat=Spring Boot)
announcement - icon

Refactor Java code safely — and automatically — with OpenRewrite.

Refactoring big codebases by hand is slow, risky, and easy to put off. That’s where OpenRewrite comes in. The open-source framework for large-scale, automated code transformations helps teams modernize safely and consistently.

Each month, the creators and maintainers of OpenRewrite at Moderne run live, hands-on training sessions — one for newcomers and one for experienced users. You’ll see how recipes work, how to apply them across projects, and how to modernize code with confidence.

Join the next session, bring your questions, and learn how to automate the kind of work that usually eats your sprint time.

Course – LJB – NPI EA (cat = Core Java)
announcement - icon

Code your way through and build up a solid, practical foundation of Java:

>> Learn Java Basics

1. Introduction

GraphQL is a query and manipulation language for web APIs. One of the libraries that originated to make working with GraphQL more seamless is SPQR.

In this tutorial, we’ll learn the basics of GraphQL SPQR and see it in action in a simple Spring Boot project.

2. What Is GraphQL SPQR?

GraphQL is a well-known query language created by Facebook. At its core are schemas – files in which we define custom types and functions.

In the traditional approach, if we wanted to add GraphQL to our project, we would have to follow two steps. First, we’d have to add GraphQL schema files to the project. Secondly, we’d need to write respective Java POJOs representing each type from the schema. This means that we’d be maintaining the same information in two places: in the schema files and in the Java classes. Such an approach is error-prone and requires more effort in maintaining the project.

GraphQL Schema Publisher & Query Resolver, SPQR in short, originated in order to reduce the above problems – it simply generates GraphQL schemas from the annotated Java classes.

3. Introducing GraphQL SPQR with Spring Boot

To see SPQR in action, we’ll set up a simple service. We’re going to use GraphQL Spring Boot Starter and GraphQL SPQR.

3.1. Setup

Let’s start by adding the dependencies for SPQR and Spring Boot to our POM:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
    <scope>test</scope>
<dependency>
    <groupId>io.leangen.graphql</groupId>
    <artifactId>spqr</artifactId>
    <version>0.12.4</version>
</dependency>

3.2. Writing the Model Book Class

Now that we’ve added the necessary dependencies, let’s create a simple Book class:

public class Book {
    private Integer id;
    private String author;
    private String title;
}

As we can see above, it doesn’t include any SPQR annotations. This can be very useful if we don’t own the source code but would like to benefit from this library.

3.3. Writing the BookService

In order to manage the collection of books, let’s create an IBookService interface:

public interface IBookService {
    Book getBookWithTitle(String title);

    List<Book> getAllBooks();

    Book addBook(Book book);

    Book updateBook(Book book);

    boolean deleteBook(Book book);
}

Then, we’ll provide an implementation of our interface:

@Service
public class BookService implements IBookService {

    private static final Set<Book> BOOKS_DATA = initializeData();

    @Override
    public Book getBookWithTitle(String title) {
        return BOOKS_DATA.stream()
            .filter(book -> book.getTitle().equals(title))
            .findFirst()
            .orElse(null);
    }

    @Override
    public List<Book> getAllBooks() {
        return new ArrayList<>(BOOKS_DATA);
    }

    @Override
    public Book addBook(Book book) {
        BOOKS_DATA.add(book);
        return book;
    }

    @Override
    public Book updateBook(Book book) {
        BOOKS_DATA.removeIf(b -> Objects.equals(b.getId(), book.getId()));
        BOOKS_DATA.add(book);
        return book;
    }

    @Override
    public boolean deleteBook(Book book) {
        return BOOKS_DATA.remove(book);
    }

    private static Set<Book> initializeData() {
        Book book = new Book(1, "J. R. R. Tolkien", "The Lord of the Rings");
        Set<Book> books = new HashSet<>();
        books.add(book);
        return books;
    }
}

3.4. Exposing the Service with graphql-spqr

The only thing left is creating a resolver, which will expose GraphQL mutations and queries. To do that, we’ll use two important SPQR annotations – @GraphQLMutation and @GraphQLQuery:

@Service
public class BookResolver {

    @Autowired
    IBookService bookService;

    @GraphQLQuery(name = "getBookWithTitle")
    public Book getBookWithTitle(@GraphQLArgument(name = "title") String title) {
        return bookService.getBookWithTitle(title);
    }

    @GraphQLQuery(name = "getAllBooks", description = "Get all books")
    public List<Book> getAllBooks() {
        return bookService.getAllBooks();
    }

    @GraphQLMutation(name = "addBook")
    public Book addBook(@GraphQLArgument(name = "newBook") Book book) {
        return bookService.addBook(book);
    }

    @GraphQLMutation(name = "updateBook")
    public Book updateBook(@GraphQLArgument(name = "modifiedBook") Book book) {
        return bookService.updateBook(book);
    }

    @GraphQLMutation(name = "deleteBook")
    public void deleteBook(@GraphQLArgument(name = "book") Book book) {
        bookService.deleteBook(book);
    }
}

If we don’t want to write @GraphQLArgument in every method and are satisfied with GraphQL parameters being named as the input parameters, we can compile the code with the -parameters argument.

3.5. Rest Controller

Finally, we’ll define a Spring @RestController. In order to expose the service with SPQR, we’ll configure the GraphQLSchema and GraphQL objects:

@RestController
public class GraphqlController {

    private final GraphQL graphQL;

    @Autowired
    public GraphqlController(BookResolver bookResolver) {
        GraphQLSchema schema = new GraphQLSchemaGenerator()
          .withBasePackages("com.baeldung")
          .withOperationsFromSingleton(bookResolver)
          .generate();
        this.graphQL = new GraphQL.Builder(schema)
          .build();
    }

It’s important to note that we have to register our BookResolver as a singleton.

The last task in our journey with SPQR is creating a /graphql endpoint. It’ll serve as a single point of contact with our service and will execute requested queries and mutations:

@PostMapping(value = "/graphql")
    public Map<String, Object> execute(@RequestBody Map<String, String> request, HttpServletRequest raw)
      throws GraphQLException {
        ExecutionResult result = graphQL.execute(request.get("query"));
        return result.getData();
    }
}

3.6. Result

We can check the results by inspecting the /graphql endpoint. For example, let’s retrieve all of the Book records by executing the following cURL command:

curl -g \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{"query":"{getAllBooks {id author title }}"}' \
  http://localhost:8080/graphql

3.7. Test

Once we’re done with the configuration, we can test our project. We’ll use @SpringBootTest to test our new endpoint and validate the responses. Let’s define the JUnit test and autowire the required WebTestClient:

@SpringBootTest(webEnvironment = RANDOM_PORT, classes = SpqrApp.class)
class SpqrAppIntegrationTest {

    private static final String GRAPHQL_PATH = "/graphql";

    @Autowired
    private WebTestClient webTestClient;

    @Test
    void whenGetAllBooks_thenValidResponseReturned() {
        String getAllBooksQuery = "{getAllBooks{ id title author }}";

        webTestClient.post()
          .uri(GRAPHQL_PATH)
          .contentType(MediaType.APPLICATION_JSON)
          .body(Mono.just(toJSON(getAllBooksQuery)), String.class)
          .exchange()
          .expectStatus().isOk()
          .expectBody()
          .jsonPath("$.getAllBooks").isNotEmpty();
    }

    @Test
    void whenAddBook_thenValidResponseReturned() {
        String addBookMutation = "mutation { addBook(newBook: {id: 123, author: \"J. K. Rowling\", "
          + "title: \"Harry Potter and Philosopher's Stone\"}) { id author title } }";

        webTestClient.post()
          .uri(GRAPHQL_PATH)
          .contentType(MediaType.APPLICATION_JSON)
          .body(Mono.just(toJSON(addBookMutation)), String.class)
          .exchange()
          .expectStatus().isOk()
          .expectBody()
          .jsonPath("$.addBook.id").isEqualTo("123")
          .jsonPath("$.addBook.title").isEqualTo("Harry Potter and Philosopher's Stone")
          .jsonPath("$.addBook.author").isEqualTo("J. K. Rowling");
    }

    private static String toJSON(String query) {
        try {
            return new JSONObject().put("query", query).toString();
        } catch (JSONException e) {
            throw new RuntimeException(e);
        }
    }

}

4. Using GraphQL SPQR Spring Boot Starter

The team working on SPQR has created a Spring Boot starter, which makes using it even easier. Let’s check it out!

4.1. Setup

We’ll start with adding the spqr-spring-boot-starter to our POM:

<dependency>
    <groupId>io.leangen.graphql</groupId>
    <artifactId>graphql-spqr-spring-boot-starter</artifactId>
    <version>1.0.1</version>
</dependency>

4.2. BookService

Then, we need to add two modifications to our BookService. First of all, it has to be annotated with the @GraphQLApi annotation. In addition, every method we’d like to expose in our API has to have a respective annotation:

@Service
@GraphQLApi
public class BookService implements IBookService {

    private static final Set<Book> BOOKS_DATA = initializeData();

    @GraphQLQuery(name = "getBookWithTitle")
    public Book getBookWithTitle(@GraphQLArgument(name = "title") String title) {
        return BOOKS_DATA.stream()
            .filter(book -> book.getTitle().equals(title))
            .findFirst()
            .orElse(null);
    }

    @GraphQLQuery(name = "getAllBooks", description = "Get all books")
    public List<Book> getAllBooks() {
        return new ArrayList<>(BOOKS_DATA);
    }

    @GraphQLMutation(name = "addBook")
    public Book addBook(@GraphQLArgument(name = "newBook") Book book) {
        BOOKS_DATA.add(book);
        return book;
    }

    @GraphQLMutation(name = "updateBook")
    public Book updateBook(@GraphQLArgument(name = "modifiedBook") Book book) {
        BOOKS_DATA.removeIf(b -> Objects.equals(b.getId(), book.getId()));
        BOOKS_DATA.add(book);
        return book;
    }

    @GraphQLMutation(name = "deleteBook")
    public boolean deleteBook(@GraphQLArgument(name = "book") Book book) {
        return BOOKS_DATA.remove(book);
    }

    private static Set<Book> initializeData() {
        Book book = new Book(1, "J. R. R. Tolkein", "The Lord of the Rings");
        Set<Book> books = new HashSet<>();
        books.add(book);
        return books;
    }

}

As we can see, we basically moved the code from the BookResolver to the BookService. Additionally, we don’t need the GraphqlController class – a /graphql endpoint will be added automatically.

5. Summary

GraphQL is an exciting framework and an alternative to traditional RESTful endpoints. While offering a lot of flexibility, it can also add some tedious tasks such as maintaining schema files. SPQR aspires to make working with GraphQL easier and less error-prone.

In this article, we saw how to add SPQR to the existing POJOs and configure it to serve queries and mutations. Then, we saw a new endpoint in action in GraphQL. Lastly, we verified our application’s behavior using Spring’s testing support.

The code backing this article is available on GitHub. Once you're logged in as a Baeldung Pro Member, start learning and coding on the project.
Baeldung Pro – NPI EA (cat = Baeldung)
announcement - icon

Baeldung Pro comes with both absolutely No-Ads as well as finally with Dark Mode, for a clean learning experience:

>> Explore a clean Baeldung

Once the early-adopter seats are all used, the price will go up and stay at $33/year.

eBook – HTTP Client – NPI EA (cat=HTTP Client-Side)
announcement - icon

The Apache HTTP Client is a very robust library, suitable for both simple and advanced use cases when testing HTTP endpoints. Check out our guide covering basic request and response handling, as well as security, cookies, timeouts, and more:

>> Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

Course – LS – NPI EA (cat=REST)

announcement - icon

Get started with Spring Boot and with core Spring, through the Learn Spring course:

>> CHECK OUT THE COURSE

Partner – Moderne – NPI EA (tag=Refactoring)
announcement - icon

Modern Java teams move fast — but codebases don’t always keep up. Frameworks change, dependencies drift, and tech debt builds until it starts to drag on delivery. OpenRewrite was built to fix that: an open-source refactoring engine that automates repetitive code changes while keeping developer intent intact.

The monthly training series, led by the creators and maintainers of OpenRewrite at Moderne, walks through real-world migrations and modernization patterns. Whether you’re new to recipes or ready to write your own, you’ll learn practical ways to refactor safely and at scale.

If you’ve ever wished refactoring felt as natural — and as fast — as writing code, this is a good place to start.

eBook Jackson – NPI EA – 3 (cat = Jackson)