query bookDetails {
bookById(id: "book-1") {
id
name
pageCount
author {
firstName
lastName
}
}
}
构建 GraphQL 服务
Spring for GraphQL 为基于 GraphQL Java 构建的 Spring 应用程序提供支持。
本指南将引导您完成使用 Spring for GraphQL 在 Java 中创建 GraphQL 服务的过程。
您将构建什么
您将构建一个服务,该服务将在 https://127.0.0.1:8080/graphql
处接收 GraphQL 请求。
您需要什么
-
大约 15 分钟
-
您喜欢的文本编辑器或 IDE
-
Java 17 或更高版本
-
您也可以将代码直接导入您的 IDE
如何完成本指南
与大多数 Spring 入门指南 一样,您可以从头开始并完成每个步骤,或者可以绕过您已经熟悉的基本设置步骤。无论哪种方式,您最终都会获得可运行的代码。
要从头开始,请继续执行 使用 Spring Initializr 开始。
要跳过基础知识,请执行以下操作
-
下载 并解压缩本指南的源代码存储库,或使用 Git 克隆它:
git clone https://github.com/spring-guides/gs-graphql-server.git
-
进入
gs-graphql-server/initial
-
跳至 GraphQL 的简要介绍。
完成后,您可以将您的结果与 gs-graphql-server/complete
中的代码进行比较。
使用 Spring Initializr 开始
如果您愿意,可以使用此 预填充的 Spring Initializr 链接 加载正确的设置。否则,请继续手动设置 Initializr。
手动初始化项目
-
导航到 https://start.spring.io。此服务会引入应用程序所需的所有依赖项,并为您完成大部分设置工作。
-
选择 Gradle 或 Maven 以及您要使用的语言。本指南假设您选择了 Java。
-
单击依赖项并选择Spring for GraphQL 和Spring Web。
-
单击生成。
-
下载生成的 ZIP 文件,该文件是使用您选择的选项配置的 GraphQL 应用程序的存档。
如果您的 IDE 集成了 Spring Initializr,则可以从 IDE 中完成此过程。 |
您也可以从 GitHub 分叉项目并在您的 IDE 或其他编辑器中打开它。 |
GraphQL 的简要介绍
GraphQL 是一种从服务器检索数据的查询语言。它是 REST、SOAP 或 gRPC 的替代方案。在本教程中,我们将查询在线商店后端中特定书籍的详细信息。
这是一个您可以发送到 GraphQL 服务器以检索书籍详细信息的示例请求
此 GraphQL 请求表示
-
对 id 为“book-1”的书籍执行查询
-
对于书籍,返回 id、name、pageCount 和 author
-
对于作者,返回 firstName 和 lastName
响应为 JSON 格式。例如
{
"bookById": {
"id":"book-1",
"name":"Effective Java",
"pageCount":416,
"author": {
"firstName":"Joshua",
"lastName":"Bloch"
}
}
}
GraphQL 的一个重要特性是它定义了一种模式语言,并且它是静态类型的。服务器确切地知道请求可以查询哪些类型的对象以及这些对象包含哪些字段。此外,客户端可以内省服务器以请求模式详细信息。
本教程中的“模式”一词指的是“GraphQL 模式”,与其他模式(如“JSON 模式”或“数据库模式”)无关。 |
上述查询的模式为
type Query {
bookById(id: ID): Book
}
type Book {
id: ID
name: String
pageCount: Int
author: Author
}
type Author {
id: ID
firstName: String
lastName: String
}
本教程将重点介绍如何在 Java 中使用此模式实现 GraphQL 服务器。
我们只是触及了 GraphQL 可能性的皮毛。可以在 GraphQL 官方页面 上找到更多信息。
我们的示例 API:获取书籍详细信息
以下是用 Spring for GraphQL 创建服务器的主要步骤
-
定义 GraphQL 模式
-
实现用于获取查询实际数据的逻辑
我们的示例应用程序将是一个简单的 API,用于获取特定书籍的详细信息。它并非旨在成为一个全面的 API。
模式
在您之前准备好的 Spring for GraphQL 应用程序中,将一个名为 schema.graphqls
的新文件添加到 src/main/resources/graphql
文件夹中,内容如下
type Query {
bookById(id: ID): Book
}
type Book {
id: ID
name: String
pageCount: Int
author: Author
}
type Author {
id: ID
firstName: String
lastName: String
}
每个 GraphQL 模式都有一个顶级 Query
类型,其下的字段是应用程序公开的查询操作。此处,模式定义了一个名为 bookById
的查询,该查询返回特定书籍的详细信息。
它还定义了类型 Book
,其中包含字段 id
、name
、pageCount
和 author
,以及类型 Author
,其中包含字段 firstName
和 lastName
。
上面用于描述模式的领域特定语言称为模式定义语言或 SDL。有关更多详细信息,请参阅 GraphQL 文档。 |
数据来源
GraphQL 的一个关键优势在于数据可以来自任何地方。数据可以来自数据库、外部服务或静态内存列表。
为了简化教程,书籍和作者数据将来自其各自类中的静态列表。
创建书籍和作者数据源
现在让我们在主应用程序包中(紧挨着 GraphQlServerApplication
)创建 Book
和 Author
类。使用以下内容作为其内容
package com.example.graphqlserver;
import java.util.Arrays;
import java.util.List;
public record Book (String id, String name, int pageCount, String authorId) {
private static List<Book> books = Arrays.asList(
new Book("book-1", "Effective Java", 416, "author-1"),
new Book("book-2", "Hitchhiker's Guide to the Galaxy", 208, "author-2"),
new Book("book-3", "Down Under", 436, "author-3")
);
public static Book getById(String id) {
return books.stream()
.filter(book -> book.id().equals(id))
.findFirst()
.orElse(null);
}
}
package com.example.graphqlserver;
import java.util.Arrays;
import java.util.List;
public record Author (String id, String firstName, String lastName) {
private static List<Author> authors = Arrays.asList(
new Author("author-1", "Joshua", "Bloch"),
new Author("author-2", "Douglas", "Adams"),
new Author("author-3", "Bill", "Bryson")
);
public static Author getById(String id) {
return authors.stream()
.filter(author -> author.id().equals(id))
.findFirst()
.orElse(null);
}
}
添加用于获取数据的代码
Spring for GraphQL 提供了一个 基于注释的编程模型。使用控制器注释的方法,我们可以声明如何为特定的 GraphQL 字段获取数据。
将以下内容添加到主应用程序包中的 BookController.java
中,紧挨着 Book
和 Author
package com.example.graphqlserver;
import org.springframework.graphql.data.method.annotation.Argument;
import org.springframework.graphql.data.method.annotation.QueryMapping;
import org.springframework.graphql.data.method.annotation.SchemaMapping;
import org.springframework.stereotype.Controller;
@Controller
public class BookController {
@QueryMapping
public Book bookById(@Argument String id) {
return Book.getById(id);
}
@SchemaMapping
public Author author(Book book) {
return Author.getById(book.authorId());
}
}
通过定义一个名为 bookById
并用 @QueryMapping
注释的方法,此控制器声明了如何获取在 Query 类型下定义的 Book
。查询字段由方法名称确定,但也可以在注释本身中声明。
Spring for GraphQL 使用 RuntimeWiring.Builder 将每个这样的控制器方法注册为 GraphQL Java graphql.schema.DataFetcher 。DataFetcher 提供了获取查询或任何模式字段数据的逻辑。GraphQL 的 Spring Boot 启动器具有自动配置功能,可以自动执行此注册。 |
在 GraphQL Java 引擎中,DataFetchingEnvironment
提供对特定于字段的参数值的映射的访问。使用 @Argument
注释将参数绑定到目标对象并注入到控制器方法中。默认情况下,方法参数名称用于查找参数,但也可以在注释本身中指定。
此 bookById
方法定义了如何获取特定的 Book
,但没有处理获取相关的 Author
。如果请求要求提供作者信息,GraphQL Java 将需要获取此字段。
@SchemaMapping
注释将处理程序方法映射到 GraphQL 模式中的字段,并将其声明为该字段的 DataFetcher
。字段名称默认为方法名称,类型名称默认为注入到方法中的源/父对象的简单类名。在此示例中,字段默认为 author
,类型默认为 Book
。
有关更多信息,请参阅 Spring for GraphQL 注释控制器功能的文档。
这就是我们需要的所有代码!
让我们运行我们的第一个查询。
运行我们的第一个查询
启用 GraphiQL Playground
GraphiQL 是一个有用的可视化界面,用于编写和执行查询,以及更多其他功能。通过将此配置添加到 application.properties
文件中来启用 GraphiQL。
spring.graphql.graphiql.enabled=true
启动应用程序
启动您的 Spring 应用程序。导航到 https://127.0.0.1:8080/graphiql。
运行查询
输入查询,然后单击窗口顶部的播放按钮。
query bookDetails {
bookById(id: "book-1") {
id
name
pageCount
author {
id
firstName
lastName
}
}
}
您应该会看到类似于以下内容的响应。
恭喜,您已经构建了一个 GraphQL 服务并执行了您的第一个查询!借助 Spring for GraphQL,您只需几行代码即可实现这一目标。
测试
Spring for GraphQL 在 spring-graphql-test
工件中提供了 GraphQL 测试的辅助工具。我们已经将此工件作为 Spring Initializr 生成的项目的一部分包含在内。
彻底测试 GraphQL 服务需要具有不同范围的测试。在本教程中,我们将编写一个 @GraphQlTest
切片测试,该测试侧重于单个控制器。还有其他辅助工具可帮助进行完整的端到端集成测试和集中的服务器端测试。有关完整详细信息,请参阅 Spring for GraphQL 测试文档 以及 Spring Boot 文档中的 Spring for GraphQL 测试的自动配置。
让我们编写一个控制器切片测试,该测试验证几分钟前在 GraphiQL Playground 中请求的相同 bookDetails
查询。
将以下内容添加到测试文件 BookControllerTests.java
中。将此文件保存在 src/test/java/com/example/graphqlserver/
文件夹内的某个位置。
package com.example.graphqlserver;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.graphql.GraphQlTest;
import org.springframework.graphql.test.tester.GraphQlTester;
@GraphQlTest(BookController.class)
public class BookControllerTests {
@Autowired
private GraphQlTester graphQlTester;
@Test
void shouldGetFirstBook() {
this.graphQlTester
.documentName("bookDetails")
.variable("id", "book-1")
.execute()
.path("bookById")
.matchesJson("""
{
"id": "book-1",
"name": "Effective Java",
"pageCount": 416,
"author": {
"firstName": "Joshua",
"lastName": "Bloch"
}
}
""");
}
}
此测试引用了一个类似于我们在 GraphiQL Playground 中使用的 GraphQL 查询。它使用 $id
参数化,使其可重用。将此查询添加到位于 src/test/resources/graphql-test
中的 bookDetails.graphql
文件中。
query bookDetails($id: ID) {
bookById(id: $id) {
id
name
pageCount
author {
id
firstName
lastName
}
}
}
运行测试并验证结果是否与在 GraphiQL Playground 中手动请求的 GraphQL 查询相同。
@GraphQlTest
注解用于编写控制器切片测试,这些测试专注于单个控制器。@GraphQlTest
自动配置 Spring for GraphQL 基础设施,无需任何传输或服务器参与。自动配置使我们能够通过跳过样板代码来更快地编写测试。由于这是一个集中的切片测试,因此只会扫描有限数量的 Bean,包括 @Controller
和 RuntimeWiringConfigurer
。有关扫描的 Bean 列表,请参阅文档。
GraphQlTester
是一个契约,声明了测试 GraphQL 请求的通用工作流程,独立于传输。在我们的测试中,我们提供了一个带有 documentName
和所需变量的文档,然后 execute
请求。然后,我们使用其 JSON 路径选择响应的一部分,并断言此位置的 JSON 与预期结果匹配。
恭喜!在本教程中,您构建了一个 GraphQL 服务,运行了第一个查询,并编写了第一个 GraphQL 测试!
进一步阅读
示例源代码
本指南是与GraphQL Java团队合作编写的。非常感谢Donna Zhou、Brad Baker和Andreas Marek!本教程的源代码可以在GitHub上找到。
Stack Overflow 问题
您可以在Stack Overflow 上使用 spring-graphql 标签提出问题。
想编写新的指南或为现有指南做出贡献?查看我们的贡献指南。
所有指南均以代码的 ASLv2 许可证和署名-非衍生作品创作共用许可证发布。 |