HTTP & Web

Implementing a GraphQL server

This document will show you how to implement a GraphQL server.

What you will build

You will build a GraphQL server to manage your personal tasks (a.k.a. the todo list).

The application consists in a few files:

  1. the tasks.graphqls schema file

  2. the Task data class

  3. the GraphQLVerticle class

What you need

  • A text editor or IDE

  • Java 17 or higher

  • Maven or Gradle

Create a project

The code of this project contains Maven and Gradle build files that are functionally equivalent.

Using Maven

Here is the content of the pom.xml file you should be using:

Using Gradle

Assuming you use Gradle with the Kotlin DSL, here is what your build.gradle.kts file should look like:

Managing tasks with Vert.x Web and GraphQL

Creating the schema

First things first, let’s create a GraphQL schema for the task management app:

GraphQL Schema src/main/resources/tasks.graphqls
type Task {
  id: String
  description: String
  completed: Boolean
}

type Query {
  allTasks(uncompletedOnly: Boolean = true): [Task]
}

type Mutation {
  complete(id: String!): Boolean
}

The schema defines:

  • the Task type with 3 fields: id, description and completed

  • the allTasks query that returns an array of tasks and optionally takes a parameter uncompletedOnly (defaults to true)

  • the complete mutation that takes the task id as parameter and returns a boolean indicating whether the operation was successful

Implementing the server

In the application, tasks will be modeled by the Task class:

1 a random identifier is assigned when a Task instance is created
The Task class field names must match those of the corresponding GraphQL schema type.

On startup, we shall create a few items:

Then the GraphQL-Java engine must be setup:

1 Read the schema file from classpath
2 TypeDefinitionRegistry is the GraphQL-Java runtime equivalent to the schema file definitions
3 RuntimeWiring tells GraphQL-Java how to resolve types and fetch data
4 GraphQLSchema connects the runtime type definitions with the type resolvers and data fetchers
5 Create the application’s GraphQL engine

So far, so good. Now how does one implement a data fetcher?

The allTasks data fetcher gets the uncompletedOnly parameter from the DataFetchingEnvironment. Its value is either provided by the client or, as defined in the schema file, set to true.

Do not block the Vert.x event loop in your data fetchers. In this how-to, the data set is small and comes from memory, so it’s safe to implement allTasks in a blocking fashion. When working with databases, caches or web services, make sure your data fetcher returns a CompletionStage. For further details, please refer to the The Vert.x Web GraphQL Handler documentation.

The code for the complete mutation is not much different:

It gets the id parameter provided by the client, then looks up for the corresponding task. If a task is found, it is updated and the mutation returns true to indicate success.

Almost there! Now let’s put things together in the verticle start method:

1 Create Vert.x Web GraphQL handler for the GraphQL-Java object
2 Define a catch-all Vert.x Web route and set the BodyHandler (required to handle POST requests bodies)
3 Define a Vert.x Web route for GraphQL queries and set the GraphQL handler

Running the application

The GraphQLVerticle needs a main method:

1 Create a Vertx context
2 Deploy GraphQLVerticle

Then you can run the application:

  • straight from your IDE or,

  • with Maven: mvn compile exec:java, or

  • with Gradle: ./gradlew run (Linux, macOS) or gradlew run (Windows).

The following examples use the HTTPie command line HTTP client. Please refer to the installation documentation if you don’t have it installed on your system yet.

Listing uncompleted tasks

To list uncompleted tasks, open your terminal and execute this:

http :8080/graphql query='
query {
  allTasks {
    id,
    description
  }
}'

You should see something like:

{
    "data": {
        "allTasks": [
            {
                "description": "Learn GraphQL",
                "id": "4a9f53fd-584f-4169-b725-6b320043db8b"
            },
            {
                "description": "Profit",
                "id": "03770db5-a8ad-44b3-ad6e-6fe979015088"
            },
            {
                "description": "Build awesome GraphQL server",
                "id": "6b000f72-8aa9-4f4e-9539-5da2ab11cd94"
            }
        ]
    }
}

Completing a task

To complete the Learn GraphQL task:

http :8080/graphql query='
mutation {
  complete(id: "4a9f53fd-584f-4169-b725-6b320043db8b")
}'
The id used in the query above must be changed to the value generated by your application.

After the task is completed, you will see:

{
    "data": {
        "complete": true
    }
}

Retrieving all tasks

If you need to retrieve all tasks, including those already completed, make sure to set the uncompletedOnly parameter to false in the allTasks query:

http :8080/graphql query='
query {
  allTasks(uncompletedOnly: false) {
    id
    description
    completed
  }
}'

The expected output is:

{
    "data": {
        "allTasks": [
            {
                "completed": true,
                "description": "Learn GraphQL",
                "id": "4a9f53fd-584f-4169-b725-6b320043db8b"
            },
            {
                "completed": false,
                "description": "Profit",
                "id": "03770db5-a8ad-44b3-ad6e-6fe979015088"
            },
            {
                "completed": false,
                "description": "Build awesome GraphQL server",
                "id": "6b000f72-8aa9-4f4e-9539-5da2ab11cd94"
            }
        ]
    }
}

Summary

This document covered:

  1. the Vert.x Web GraphQL Handler setup,

  2. implementing simple query and mutation data fetchers.