<?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>
构建 GraphQL 服务端应用
本指南将向你展示如何使用 Vert.x Web GraphQL 模块快速搭建高性能 GraphQL 数据查询与变更服务。
你将构建什么
你将构建一个 GraphQL 服务端,用于管理个人待办事项任务(Todo List)。
该应用程序由以下几个核心文件组成:
-
tasks.graphqls:GraphQL Schema 契约定义文件; -
Task:数据实体 POJO 类; -
GraphQLVerticle:驱动服务的 Vert.x Verticle。
前置要求
-
文本编辑器或 IDE
-
Java 17 或更高版本
-
Maven 或 Gradle
创建工程项目
本项目代码提供了 Maven 与 Gradle 两种构建方式,二者完全等价。
使用 Maven
以下是推荐的 pom.xml 构建配置:
pom.xml使用 Gradle
若使用带有 Kotlin DSL 的 Gradle,对应的 build.gradle.kts 文件如下:
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")
}
使用 Vert.x Web 与 GraphQL 管理待办事项
定义 GraphQL Schema 契约
首先,我们需要在资源目录中定义待办应用的 GraphQL Schema:
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
}
该 Schema 定义了:
-
Task类型:包含id、description(描述)与completed(是否已完成)三个字段; -
allTasks查询(Query):返回任务列表,支持传入可选的uncompletedOnly参数(默认值为true,即仅查询未完成事项); -
complete变更(Mutation):接收任务的id作为参数,并返回布尔值表示该任务标记完成是否成功。
编写服务端核心逻辑
在应用层,待办任务由 Task 类进行映射:
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 | 创建 Task 实例时自动为其生成并分配随机唯一标识。 |
Task 类中的字段名称必须与 GraphQL Schema 中声明的字段名称严格保持一致。
|
在应用启动时,我们预置几条初始任务数据:
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()));
}
随后,我们需要配置并组装 GraphQL-Java 引擎:
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 | 从 Classpath 读取并解析 tasks.graphqls 模式文件; |
| 2 | TypeDefinitionRegistry 是 GraphQL-Java 在运行期对 Schema 模式定义的内存表示; |
| 3 | RuntimeWiring 告诉 GraphQL-Java 如何解析具体的数据类型并关联数据拉取器(DataFetcher); |
| 4 | GraphQLSchema 将运行期类型定义与具体的数据解析器深度绑定连接; |
| 5 | 最终实例化应用程序的 GraphQL 核心引擎。 |
那么,如何实现一个数据拉取器(DataFetcher)呢?
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());
}
allTasks 数据拉取器从 DataFetchingEnvironment 上下文中获取客户端传入的 uncompletedOnly 参数。如果客户端未传,则采用 Schema 中预设的默认值 true。
切勿在数据拉取器中阻塞 Vert.x Event Loop 事件循环! 在本实战指南中,数据集规模极小且直接驻留内存,因此同步实现 allTasks 是安全的。但在生产环境中与数据库、缓存或第三方 Web 服务交互时,请务必确保你的 DataFetcher 返回异步的 CompletionStage 或 Vert.x Future。详情请参阅 Vert.x Web GraphQL Handler 官方手册。
|
complete 数据变更的实现逻辑与此类似:
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;
}
它从客户端提取参数中的任务 id,检索匹配的任务并将其状态置为已完成,最后返回 true 告知调用方操作成功。
大功告成!现在我们在 Verticle 的 start 方法中将所有组件装配在一起:
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 | 为 GraphQL-Java 实例创建对应的 Vert.x Web GraphQLHandler 处理器; |
| 2 | 声明一个全匹配路由并挂载 BodyHandler(处理 HTTP POST 请求正文所必需); |
| 3 | 为 /graphql 路径定义专属路由,并将 GraphQL 处理器挂载就位。 |
运行与实测
运行该应用非常直观:
-
直接在 IDE 中运行
GraphQLVerticle.java中的main方法; -
或者通过 Maven 运行:
mvn compile exec:java; -
或者通过 Gradle 运行:
./gradlew run。
以下示例使用流行的命令行 HTTP 客户端 HTTPie 进行测试演示:
1. 查询所有未完成的待办任务
打开终端并执行 GraphQL 查询:
http :8080/graphql query='
query {
allTasks {
id,
description
}
}'
你将看到结构化 JSON 响应:
{
"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"
}
]
}
}
2. 标记完成一条任务
将 Learn GraphQL 任务标记为完成:
http :8080/graphql query='
mutation {
complete(id: "4a9f53fd-584f-4169-b725-6b320043db8b")
}'
请将上述查询中的 id 替换为你实际查询返回的真实 ID。
|
执行后将返回:
{
"data": {
"complete": true
}
}
3. 检索全量待办任务(含已完成)
将 uncompletedOnly 参数指定为 false:
http :8080/graphql query='
query {
allTasks(uncompletedOnly: false) {
id
description
completed
}
}'
你将看到包含已完成状态("completed": true)的完整任务集合。
本文总结
通过本指南,我们掌握了:
-
Vert.x Web 与 GraphQL-Java 的桥接集成流程;
-
定义清晰的 GraphQL Schema 类型体系;
-
实现非阻塞的 Query 查询与 Mutation 变更拉取器。