GraphQL tutorial for Spring Boot users

This article is a walk though a GraphQL application using Spring Boot. We will cover the basics of GraphQL and then we will show how to code and test an example application.

GraphQL is a query language known for having a highly typed schema, and for providing developers with the information they need for creating new applications and transferring data. The GraphQL schema identifies input parameters and possible answers quite clearly, reducing the confusion and rework often required with other API models to ensure consistent results.

Schemas, resolvers and other common GraphQL terms

API developers use GraphQL to create a schema that describes all the possible data that clients might query via the specified service.

A GraphQL schema consists of object types that define the types of objects that can be queried and the types of fields available within it. As queries are received, GraphQL validates them against the schema, then executes them.

The API developer adds the schema fields to a function called resolver. During execution, the resolver generates the value.

From the client’s point of view, GraphQL deals mainly with queries and mutations. Compared with the standard CRUD model (create, read, update and delete), a Query would be equivalent to read. All other elements (create, update and delete) are handled by Mutation.

Set up the Spring Boot Project

Firstly, we will create our Spring Boot project. When using the Spring Boot Initializr, simply add the Web dependency and the GraphQL dependency. Click on Add Dependencies and download the project.

Next, import the project to your IDE.

Defining our Schema

Next step will be defining the GraphQL schema which follows the Model of our Domain. The GraphQL specification defines a human-readable schema definition language (or SDL) that you use to define your schema and store it as a string. Here is our schema:

type Book {
    id: ID!
    title: String
    author: Author
}

type Author {
    id: ID!
    name: String
    books: [Book]
}

type Query {
    allBooks: [Book]
    findOne(id: ID!): Book
    allAuthors: [Author]
}

type Mutation {
     createBook(
         title: String
         author: String): Boolean!
}

As you can see, our Schema defines two main types: Book and Author. Besides, the schema includes a set of Query and one Mutation.

The Query allBooks returns the list of Books. The query findOne return a Book by its id. Finally, the query allAuthors returns all Book Authors.

The Mutation createBook adds a new Book using as input parameter the Title and the Author.

For learning purpose, we will use a simple set of objects stored in memory. Therefore, we will add the following Java Records to our project. Here is the Author Record:

public record Author(Integer id, String name) {
}

Next, here is the Book Record:

public record Book(Integer id, String title, Author author) {

}

Records, available since Java 14, are immutable data classes that require only the type and name of fields. Using records, a public constructor, with an argument for each field, is generated for us. Besides, we also receive public getters methods out of the box.

Coding the Controller

Client accessing our applications require a Controller. Here is the BookController:

@Controller
public class BookController {

    private final BookRepository bookRepository;


    public BookController(BookRepository bookRepository) {
        this.bookRepository = bookRepository;
    }


    @SchemaMapping(typeName = "Query",value = "allBooks")
    public List<Book> findAll() {
        return bookRepository.findAll();
    }

    @QueryMapping
    public Book findOne(@Argument Integer id) {
        return bookRepository.findOne(id);
    }

    @MutationMapping
    public boolean createBook(@Argument("title") String title, @Argument("author") String author) {
        return bookRepository.add(title, author);
    }

}

To start the Spring Boot application, we will also add a basic Application Class:

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

}

Finally, here is the application.properties:

spring.graphql.graphiql.enabled=true
spring.graphql.cors.allowed-origins=*
spring.graphql.schema.printer.enabled=true

With the above properties we will be able to activate the GraphQL UI to test our example. Also we enabled cross-origin resource sharing (CORS) and schema debug.

Testing the application

Having enabled the UI of GraphQL, start the application and connect to http://localhost:8080/graphiql?path=/graphql#

Next, you can Query allBooks using the following Query:

query {
  allBooks {
    title

    author {
       name
    }
  }
}

Here is the output of our query in the GraphiQL:

To find a single Book, you can use the findOne Query which requires an integer (id) as parameter:

query {
      findOne(id: 1) {
        title

        author {
          name
        }
      }
}

Finally, to add a book using the mutation CreateBook:

mutation CreateBook($title:String, $author:String) {
  createBook(title: $title, author: $author)
}
{
  "title": $title,
  "author": $author

}

As you can see from the above mutation, we are using variables in our mutation expression. To allow the GrapQL server to resolve those variables, we will set them as Query Variables:

Conclusion

This article was a walk through an example GraphQL application in Spring Boot. You can find the source code for this tutorial here: https://github.com/fmarchioni/masterspringboot/tree/master/graphql/graphql-demo

Leave a Reply