<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>io.vertx.howtos</groupId>
<artifactId>graphql-howto</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<vertx.version>5.0.0.CR2</vertx.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-stack-depchain</artifactId>
<version>${vertx.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-web-graphql</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.13.0</version>
<configuration>
<release>17</release>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.5.0</version>
<configuration>
<mainClass>io.vertx.howtos.graphql.GraphQLVerticle</mainClass>
</configuration>
</plugin>
</plugins>
</build>
</project> 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:
-
the
tasks.graphqlsschema file -
the
Taskdata class -
the
GraphQLVerticleclass
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:
pom.xmlUsing Gradle
Assuming you use Gradle with the Kotlin DSL, here is what your build.gradle.kts file should look like:
build.gradle.ktsplugins {
java
application
}
repositories {
mavenCentral()
}
java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(17))
}
}
dependencies {
implementation(platform("io.vertx:vertx-stack-depchain:5.0.0.CR2"))
implementation("io.vertx:vertx-web-graphql")
}
application {
mainClass.set("io.vertx.howtos.graphql.GraphQLVerticle")
} 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:
src/main/resources/tasks.graphqlstype Task {
id: String
description: String
completed: Boolean
}
type Query {
allTasks(uncompletedOnly: Boolean = true): [Task]
}
type Mutation {
complete(id: String!): Boolean
} The schema defines:
-
the
Tasktype with 3 fields:id,descriptionandcompleted -
the
allTasksquery that returns an array of tasks and optionally takes a parameteruncompletedOnly(defaults totrue) -
the
completemutation that takes the taskidas 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:
src/main/java/io/vertx/howtos/graphql/Task.javapackage io.vertx.howtos.graphql;
import java.util.UUID;
public record Task(String id, String description, boolean completed) {
public Task(String description) {
this(UUID.randomUUID().toString(), description, false); (1)
}
} | 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:
src/main/java/io/vertx/howtos/graphql/GraphQLVerticle.java private Map<String, Task> initData() {
return Stream.of(
new Task("Learn GraphQL"),
new Task("Build awesome GraphQL server"),
new Task("Profit")
).collect(toMap(Task::id, identity()));
} Then the GraphQL-Java engine must be setup:
src/main/java/io/vertx/howtos/graphql/GraphQLVerticle.java private GraphQL setupGraphQL() {
var schema = vertx.fileSystem().readFileBlocking("tasks.graphqls").toString(); (1)
var schemaParser = new SchemaParser();
var typeDefinitionRegistry = schemaParser.parse(schema); (2)
var runtimeWiring = newRuntimeWiring() (3)
.type("Query", builder -> builder.dataFetcher("allTasks", this::allTasks))
.type("Mutation", builder -> builder.dataFetcher("complete", this::complete))
.build();
var schemaGenerator = new SchemaGenerator();
var graphQLSchema = schemaGenerator.makeExecutableSchema(typeDefinitionRegistry, runtimeWiring); (4)
return GraphQL.newGraphQL(graphQLSchema).build(); (5)
} | 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?
src/main/java/io/vertx/howtos/graphql/GraphQLVerticle.java private List<Task> allTasks(DataFetchingEnvironment env) {
boolean uncompletedOnly = env.getArgument("uncompletedOnly");
return tasks.values().stream()
.filter(task -> !uncompletedOnly || !task.completed())
.collect(toList());
} 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:
src/main/java/io/vertx/howtos/graphql/GraphQLVerticle.java private boolean complete(DataFetchingEnvironment env) {
String id = env.getArgument("id");
var task = tasks.get(id);
if (task == null) {
return false;
}
tasks.put(id, new Task(task.id(), task.description(), true));
return true;
} 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:
src/main/java/io/vertx/howtos/graphql/GraphQLVerticle.java private Map<String, Task> tasks;
@Override
public Future<?> start() {
tasks = initData();
var graphQL = setupGraphQL();
var graphQLHandler = GraphQLHandler.create(graphQL); (1)
var router = Router.router(vertx);
router.route().handler(BodyHandler.create()); (2)
router.route("/graphql").handler(graphQLHandler); (3)
return vertx.createHttpServer()
.requestHandler(router)
.listen(8080);
} | 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:
src/main/java/io/vertx/howtos/graphql/GraphQLVerticle.java public static void main(String[] args) {
var vertx = Vertx.vertx(); (1)
vertx.deployVerticle(new GraphQLVerticle()).await(); (2)
System.out.println("Verticle deployed");
} | 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) orgradlew 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:
-
the Vert.x Web GraphQL Handler setup,
-
implementing simple query and mutation data fetchers.