Building a Spring Boot Application with GraphQL
This tutorial demonstrates how to create a Spring Boot project, define a GraphQL schema, implement data fetchers, expose REST endpoints, and query book data using GraphQL, highlighting the advantages of GraphQL over traditional REST APIs.
GraphQL is an API query language originally developed by Facebook that offers a more efficient, powerful, and flexible way to retrieve data compared to traditional REST APIs. This article introduces GraphQL and walks through a complete example of integrating it into a Spring Boot application.
Why use GraphQL? Unlike REST, GraphQL eliminates the need for multiple endpoints, allows clients to request exactly the fields they need, and is not tied to any specific database or storage engine, making it adaptable to existing architectures.
Creating the application – Use Spring Initializr (or IntelliJ IDEA Ultimate) to generate a Spring Boot project with dependencies such as spring-boot-starter-web, spring-boot-starter-data-jpa, hsqldb, graphql-spring-boot-starter, and graphql-java-tools. The generated pom.xml looks like:
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.4.RELEASE</version>
</parent>
<artifactId>springboot-graphql-app</artifactId>
<name>springboot-graphql-app</name>
<description>Demo project for Spring Boot with GraphQL</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.graphql-java</groupId>
<artifactId>graphql-spring-boot-starter</artifactId>
<version>3.6.0</version>
</dependency>
<dependency>
<groupId>com.graphql-java</groupId>
<artifactId>graphql-java-tools</artifactId>
<version>3.2.0</version>
</dependency>
</dependencies>Adding the endpoint – Create BookController to handle POST requests at /rest/books and delegate queries to the GraphQL service:
package graphqlapp.controller;
import graphql.ExecutionResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RequestMapping("/rest/books")
@RestController
public class BookController {
private static Logger logger = LoggerFactory.getLogger(BookController.class);
private GraphQLService graphQLService;
@Autowired
public BookController(GraphQLService graphQLService) {
this.graphQLService = graphQLService;
}
@PostMapping
public ResponseEntity<Object> getAllBooks(@RequestBody String query) {
logger.info("Entering getAllBooks@BookController");
ExecutionResult execute = graphQLService.getGraphQL().execute(query);
return new ResponseEntity<>(execute, HttpStatus.OK);
}
}Adding the model – Define a JPA entity Book representing the book data:
package graphqlapp.model;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
@Table
@Entity
public class Book {
@Id
private String isn;
private String title;
private String publisher;
private String publishedDate;
private String[] author;
public Book() {}
public Book(String isn, String title, String publisher, String publishedDate, String[] author) {
this.isn = isn;
this.title = title;
this.publisher = publisher;
this.publishedDate = publishedDate;
this.author = author;
}
// getters and setters omitted for brevity
}Creating the repository – Extend JpaRepository to provide CRUD operations:
package graphqlapp.repository;
import graphqlapp.model.Book;
import org.springframework.data.jpa.repository.JpaRepository;
public interface BookRepository extends JpaRepository<Book, String> {}Defining the GraphQL schema – Add books.graphql under src/main/resources:
schema { query: Query }
type Query {
allBooks: [Book]
book(id: String): Book
}
type Book {
isn: String
title: String
publisher: String
author: [String]
publishedDate: String
}Adding the GraphQL service – The GraphQLService loads the schema, populates an in‑memory HSQLDB with sample books, and wires data fetchers:
package graphqlapp.service;
import graphql.GraphQL;
import graphql.schema.GraphQLSchema;
import graphql.schema.idl.RuntimeWiring;
import graphql.schema.idl.SchemaGenerator;
import graphql.schema.idl.SchemaParser;
import graphql.schema.idl.TypeDefinitionRegistry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.io.File;
import java.io.IOException;
import java.util.stream.Stream;
@Service
public class GraphQLService {
private static Logger logger = LoggerFactory.getLogger(GraphQLService.class);
private BookRepository bookRepository;
private AllBooksDataFetcher allBooksDataFetcher;
private BookDataFetcher bookDataFetcher;
@Value("classpath:books.graphql")
Resource resource;
private GraphQL graphQL;
@Autowired
public GraphQLService(BookRepository bookRepository, AllBooksDataFetcher allBooksDataFetcher, BookDataFetcher bookDataFetcher) {
this.bookRepository = bookRepository;
this.allBooksDataFetcher = allBooksDataFetcher;
this.bookDataFetcher = bookDataFetcher;
}
@PostConstruct
private void loadSchema() throws IOException {
logger.info("Entering loadSchema@GraphQLService");
loadDataIntoHSQL();
File file = resource.getFile();
TypeDefinitionRegistry typeDefinitionRegistry = new SchemaParser().parse(file);
RuntimeWiring runtimeWiring = buildRuntimeWiring();
GraphQLSchema graphQLSchema = new SchemaGenerator().makeExecutableSchema(typeDefinitionRegistry, runtimeWiring);
graphQL = GraphQL.newGraphQL(graphQLSchema).build();
}
private void loadDataIntoHSQL() {
Stream.of(
new Book("1001", "The C Programming Language", "PHI Learning", "1978", new String[]{"Brian W. Kernighan (Contributor)", "Dennis M. Ritchie"}),
new Book("1002", "Your Guide To Scrivener", "MakeUseOf.com", "April 21st 2013", new String[]{"Nicole Dionisio (Goodreads Author)"}),
new Book("1003", "Beyond the Inbox: The Power User Guide to Gmail", "Kindle Edition", "November 19th 2012", new String[]{"Shay Shaked", "Justin Pot", "Angela Randall (Goodreads Author)"}),
new Book("1004", "Scratch 2.0 Programming", "Smashwords Edition", "February 5th 2015", new String[]{"Denis Golikov (Goodreads Author)"}),
new Book("1005", "Pro Git", "by Apress (first published 2009)", "2014", new String[]{"Scott Chacon"})
).forEach(book -> bookRepository.save(book));
}
private RuntimeWiring buildRuntimeWiring() {
return RuntimeWiring.newRuntimeWiring()
.type("Query", typeWiring -> typeWiring
.dataFetcher("allBooks", allBooksDataFetcher)
.dataFetcher("book", bookDataFetcher))
.build();
}
public GraphQL getGraphQL() { return graphQL; }
}Data fetchers – Implement two components that retrieve data for the schema fields:
package graphqlapp.service.datafetcher;
import graphql.schema.DataFetcher;
import graphql.schema.DataFetchingEnvironment;
import graphqlapp.model.Book;
import graphqlapp.repository.BookRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
public class AllBooksDataFetcher implements DataFetcher<List<Book>> {
private BookRepository bookRepository;
@Autowired
public AllBooksDataFetcher(BookRepository bookRepository) { this.bookRepository = bookRepository; }
@Override
public List<Book> get(DataFetchingEnvironment env) { return bookRepository.findAll(); }
}
@Component
public class BookDataFetcher implements DataFetcher<Book> {
private BookRepository bookRepository;
@Autowired
public BookDataFetcher(BookRepository bookRepository) { this.bookRepository = bookRepository; }
@Override
public Book get(DataFetchingEnvironment env) {
String isn = env.getArgument("id");
return bookRepository.findById(isn).orElse(null);
}
}Running the application – Set server.port=9002 in application.properties and start the Spring Boot app. Use Postman to send GraphQL queries to http://localhost:9002/rest/books. Example queries and their JSON responses demonstrate fetching a single book by ID, retrieving all books, and selecting specific fields, illustrating GraphQL’s ability to return only the requested data.
Conclusion – By integrating GraphQL with Spring Boot, developers gain fine‑grained control over the data returned to clients, reduce over‑fetching, and simplify API evolution compared to traditional REST endpoints.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
High Availability Architecture
Official account for High Availability Architecture.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
