Implementing GraphQL server with Spring Boot GraphQL Server.
Background
GraphQL provides the flexibility for clients to retrieve fields that are only relevant to them. In this tutorial we will explore how we can implement GraphQL server with Spring Boot.
Server Implementation
schema.graphqls
scalar Long
type Query {
findAll: [Book]
findByTitle(title: String): Book
}
type Book {
isbn: Isbn
title: String
author: Author
}
type Isbn {
ean: Long
registrationGroup: Int
registrant: Int
publication: Int
digit: Int
}
type Author {
name: Name
}
type Name {
first: String
last: String
}
This is the schema that inform our clients on available fields and queries. Next is to implement a @Controller
that
handle all requests.
Controller
@Controller
class BookResource {
private final BookRepository repository;
BookResource(BookRepository repository) {
this.repository = repository;
}
@QueryMapping
public List<Book> findAll() {
return repository.findAll();
}
@QueryMapping
public Book findByTitle(@Argument String title) {
return repository.findByTitle(title);
}
}
BookResource implements two types of services -
findAll
, a service without parameter and findByTitle
, a service with a parameter. Both services are annotated with
@QueryMapping
to indicate that they are GraphQL queries.
Verification
We will utilise @GraphQlTest
to verify our implementation.
Integration Test
@GraphQlTest(
controllers = BookResource.class,
includeFilters = @Filter(type = ANNOTATION, classes = { Configuration.class, Repository.class })
)
class BookResourceTests {
@Autowired
private GraphQlTester client;
@Test
@DisplayName("Given there are 3 books, when findAll is invoked, then return all books")
void findAll() {
client.documentName("books")
.execute()
.path("findAll")
.matchesJson(
"""
[
{
"title": "Clean Code"
},
{
"title": "Design Patterns"
},
{
"title": "The Hobbit"
}
]
"""
)
;
}
@Test
@DisplayName("Given there is a book titled The Hobbit, when findByTitle is invoked, then return the book")
void findByTitle() {
client.documentName("books")
.variable("title", "The Hobbit")
.execute()
.path("findByTitle")
.matchesJson(
"""
{
"title": "The Hobbit",
"isbn": {
"ean": 9780132350884,
"registrationGroup": 978,
"registrant": 0,
"publication": 13235088,
"digit": 4
},
"author": {
"name": {
"first": "J.R.R.",
"last": "Tolkien"
}
}
}
"""
);
}
}
Client schema definition
In order to map the response to a Java object, we need to define the schema of the response. This is done in books.graphl.
query books($title: String) {
findByTitle(title: $title) {
title
isbn {
ean
registrationGroup
registrant
publication
digit
}
author {
name {
first
last
}
}
}
findAll {
title
}
}
By executing tests implemented in BookResourceTests, we can verify that our implementation is working as expected.