GraphQL 类型 - Amazon AppSync
Amazon Web Services 文档中描述的 Amazon Web Services 服务或功能可能因区域而异。要查看适用于中国区域的差异,请参阅 中国的 Amazon Web Services 服务入门 (PDF)

本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。

GraphQL 类型

GraphQL 支持很多不同的类型。正如您在上一节中看到的一样,类型定义数据的形状或行为。它们是 GraphQL 架构的基本构建块。

类型可以分为输入和输出。输入是允许作为特殊对象类型(QueryMutation 等)的参数传入的类型,而输出类型严格用于存储和返回数据。下面列出了类型及其分类的列表:

  • 对象:对象包含描述实体的字段。例如,一个对象可能类似于 book,其中包含描述其特性的字段,如 authorNamepublishingYear 等。它们严格来说是输出类型。

  • 标量:这些是基元类型,如整数、字符串等。它们通常分配给字段。以 authorName 字段为例,可以为其分配 String 标量以存储名称,例如“John Smith”。标量可以是输入类型和输出类型。

  • 输入:输入允许您传递一组字段以作为参数。它们的结构与对象非常相似,但可以将其作为特殊对象的参数传递。输入允许您在其范围内定义标量、枚举和其他输入。输入只能是输入类型。

  • 特殊对象:特殊对象执行状态更改操作,并完成服务的大部分繁重工作。共有三种特殊对象类型:查询、变更和订阅。查询通常获取数据;变更处理数据;订阅在客户端和服务器之间打开并保持双向连接以进行持续通信。鉴于其功能,特殊对象既不是输入,也不是输出。

  • 枚举:枚举是预定义的合法值列表。如果调用枚举,则枚举值只能是其范围内定义的值。例如,如果您使用一个名为 trafficLights 的枚举以描述交通信号列表,它可能具有 redLightgreenLight 等值,但不能具有 purpleLight 值。真正的交通信号灯只有那么多信号,因此,您可以使用枚举定义它们,并在引用 trafficLight 时强制使它们成为唯一的合法值。枚举可以是输入类型和输出类型。

  • 联合/接口:联合允许您根据客户端请求的数据在请求中返回一个或多个内容。例如,如果您使用具有 title 字段的 Book 类型以及具有 name 字段的 Author 类型,您可以在这两种类型之间创建联合。如果您的客户端希望在数据库中查询“Julius Caesar”,则联合可能会从 Book title 返回 Julius Caesar(威廉·莎士比亚的戏剧),并从 Author name 返回 Julius CaesarCommentarii de Bello Gallico 的作者)。联合只能是输出类型。

    接口是对象必须实施的字段集。这有点类似于 Java 等编程语言中的接口,您必须实施接口中定义的字段。例如,假设您创建了一个名为 Book 的接口,其中包含一个 title 字段。假设您后来创建了一个名为 Novel 的类型,该类型实施 BookNovel 必须包含一个 title 字段。不过,Novel 可能还包含接口中不包含的其他字段,例如 ISBNpageCount。接口只能是输出类型。

以下几节介绍了每种类型在 GraphQL 中的工作方式。

Objects

GraphQL 对象是您在生产代码中看到的主要类型。在 GraphQL 中,您可以将对象视为不同字段的分组(类似于其他语言中的变量),每个字段由可以保存值的类型(通常是标量或另一个对象)定义。对象表示可以从服务实施中检索/处理的数据单元。

对象类型是使用 Type 关键字声明的。让我们稍微修改一下架构示例:

type Person { id: ID! name: String age: Int occupation: Occupation } type Occupation { title: String }

此处的对象类型是 PersonOccupation。每个对象具有自己的字段和自己的类型。GraphQL 的一项功能是,能够将字段设置为其他类型。您可以看到 Person 中的 occupation 字段包含 Occupation 对象类型。我们可以建立这种关联,因为 GraphQL 仅描述数据,而不描述服务实施。

标量

标量本质上是保存值的基元类型。在 Amazon AppSync 中,具有两种类型的标量:默认 GraphQL 标量和 Amazon AppSync 标量。标量通常用于存储对象类型中的字段值。默认 GraphQL 类型包括 IntFloatStringBooleanID。让我们再次使用上一示例:

type Person { id: ID! name: String age: Int occupation: Occupation } type Occupation { title: String }

挑出 nametitle 字段,两者都包含 String 标量。Name 可能返回类似于 "John Smith" 的字符串值,title 可能返回类似于 "firefighter" 的值。一些 GraphQL 实施还支持使用 Scalar 关键字并实施类型行为的自定义标量。不过,Amazon AppSync 目前不支持自定义标量。有关标量的列表,请参阅 Amazon AppSync 中的标量类型

输入

由于输入和输出类型概念,在传入参数时存在特定的限制。通常需要传入的类型(尤其是对象)受到限制。您可以使用输入类型绕过该规则。输入是包含标量、枚举和其他输入类型的类型。

输入是使用 input 关键字定义的:

type Person { id: ID! name: String age: Int occupation: Occupation } type Occupation { title: String } input personInput { id: ID! name: String age: Int occupation: occupationInput } input occupationInput { title: String }

正如您看到的一样,我们可以使用模仿原始类型的单独输入。这些输入通常在您的字段操作中使用,如下所示:

type Person { id: ID! name: String age: Int occupation: Occupation } type Occupation { title: String } input occupationInput { title: String } type Mutation { addPerson(id: ID!, name: String, age: Int, occupation: occupationInput): Person }

请注意,我们仍然可以传递 occupationInput(替代 Occupation)以创建 Person

这只是输入的一种场景。它们不一定需要以 1:1 的方式复制对象;在生产代码中,您很可能不会像这样使用该参数。一种利用 GraphQL 架构的很好做法是,仅定义需要作为参数输入的内容。

此外,可以在多个操作中使用相同的输入,但我们不建议这样做。理想情况下,每个操作应包含自己的唯一输入副本,以防架构的要求发生变化。

特殊对象

GraphQL 为特殊对象保留一些关键字,这些对象定义架构如何检索/处理数据的一些业务逻辑。最多可以在架构中包含其中的关键字之一。它们充当客户端对 GraphQL 服务运行的所有请求的数据的入口点。

也可以使用 type 关键字定义特殊对象。尽管它们的使用方式与常规对象类型不同,但它们的实施非常相似。

Queries

查询与 GET 操作非常相似,因为它们执行只读获取以从源中获取数据。在 GraphQL 中,Query 定义向您的服务器发出请求的客户端的所有入口点。在您的 GraphQL 实施中始终具有一个 Query

以下是我们在以前的架构示例中使用的 Query 和修改的对象类型:

type Person { id: ID! name: String age: Int occupation: Occupation } type Occupation { title: String } type Query { people: [Person] }

我们的 Query 包含一个名为 people 的字段,该字段从数据源中返回 Person 实例列表。假设我们需要更改应用程序的行为,现在我们需要返回仅包含 Occupation 实例的列表以用于某些单独的用途。我们可以直接将其添加到查询中:

type Query { people: [Person] occupations: [Occupation] }

在 GraphQL 中,我们可以将查询视为单一请求来源。正如您看到的一样,这可能比使用不同终端节点(.../api/1/people.../api/1/occupations)实现相同目标的 RESTful 实施简单得多。

假设我们具有该查询的解析器实施,我们现在可以执行实际的查询。虽然 Query 类型存在,但我们必须明确调用该类型,才能在应用程序的代码中运行。可以使用 query 关键字完成该操作:

query getItems { people { name } occupations { title } }

正如您看到的一样,该查询命名为 getItems 并返回 peoplePerson 对象列表)和 occupationsOccupation 对象列表)。在 people 中,我们仅返回每个 Personname 字段,同时返回每个 Occupationtitle 字段。响应可能如下所示:

{ "data": { "people": [ { "name": "John Smith" }, { "name": "Andrew Miller" }, . . . ], "occupations": [ { "title": "Firefighter" }, { "title": "Bookkeeper" }, . . . ] } }

该示例响应说明了数据如何遵循查询的形状。检索的每个条目在该字段的范围内列出。peopleoccupations 是作为单独的列表返回的。虽然这很有用,但修改查询以返回人员姓名和职业的列表可能会更方便:

query getItems { people { name occupation { title } }

这是合法的修改,因为我们的 Person 类型包含 Occupation 类型的 occupation 字段。在 people 范围内列出时,我们将返回每个 Personname 及其按 title 关联的 Occupation。响应可能如下所示:

} "data": { "people": [ { "name": "John Smith", "occupation": { "title": "Firefighter" } }, { "name": "Andrew Miller", "occupation": { "title": "Bookkeeper" } }, . . . ] } }
Mutations

变更类似于 PUTPOST 等状态更改操作。它们执行写入操作以修改源中的数据,然后获取响应。它们定义数据修改请求的入口点。与查询不同,根据项目的需求,变更可能会包含在架构中,也可能不会包含在架构中。以下是架构示例中的变更:

type Mutation { addPerson(id: ID!, name: String, age: Int): Person }

addPerson 字段表示将 Person 添加到数据源的一个入口点。addPerson 是字段名称;idnameage 是参数;Person 是返回类型。回顾一下 Person 类型:

type Person { id: ID! name: String age: Int occupation: Occupation }

我们添加了 occupation 字段。不过,我们不能直接将该字段设置为 Occupation,因为不能将对象作为参数传入;它们严格来说是输出类型。我们应该将具有相同字段的输入作为参数传递:

input occupationInput { title: String }

在创建新的 Person 实例时,我们也可以轻松更新 addPerson 以将其包含为参数:

type Mutation { addPerson(id: ID!, name: String, age: Int, occupation: occupationInput): Person }

以下是更新的架构:

type Person { id: ID! name: String age: Int occupation: Occupation } type Occupation { title: String } input occupationInput { title: String } type Mutation { addPerson(id: ID!, name: String, age: Int, occupation: occupationInput): Person }

请注意,occupationoccupationInput 传入 title 字段,以完成创建 Person 而不是原始 Occupation 对象。假设我们具有 addPerson 的解析器实施,我们现在可以执行实际的变更。虽然 Mutation 类型存在,但我们必须明确调用该类型,才能在应用程序的代码中运行。可以使用 mutation 关键字完成该操作:

mutation createPerson { addPerson(id: ID!, name: String, age: Int, occupation: occupationInput) { name age occupation { title } } }

该变更命名为 createPersonaddPerson 是操作。要创建新的 Person,我们可以输入 idnameageoccupation 的参数。在 addPerson 的范围内,我们还可以看到其他字段,如 nameage 等。这是您的响应;这些是 addPerson 操作完成后返回的字段。以下是示例的最后一部分:

mutation createPerson { addPerson(id: "1", name: "Steve Powers", age: "50", occupation: "Miner") { id name age occupation { title } } }

在使用该变更时,结果可能如下所示:

{ "data": { "addPerson": { "id": "1", "name": "Steve Powers", "age": "50", "occupation": { "title": "Miner" } } } }

正如您看到的一样,响应使用与变更中定义的相同格式返回我们请求的值。最好返回所有修改的值,以减少混乱以及将来需要进行更多查询。变更允许您在其范围内包含多个操作。它们按照变更中列出的顺序依次运行。例如,如果我们创建另一个名为 addOccupation 的操作以将职务添加到数据源中,我们可以在变更中的 addPerson 之后调用该操作。先处理 addPerson,然后处理 addOccupation

Subscriptions

订阅使用 WebSockets 在服务器与其客户端之间打开持久的双向连接。通常,客户端订阅或侦听服务器。每次服务器进行服务器端更改或执行事件时,订阅的客户端都会收到更新。如果订阅了多个客户端,并且需要向它们通知服务器或其他客户端中发生的更改,这种类型的协议是非常有用的。例如,可以使用订阅更新社交媒体源。可能具有两个用户(用户 A 和用户 B),他们订阅了自动通知更新,以在每次收到直接消息时收到通知。客户端 A 上的用户 A 可能向客户端 B 上的用户 B 发送直接消息。用户 A 的客户端将发送直接消息,而服务器处理该消息。然后,服务器向用户 B 的账户发送直接消息,同时向客户端 B 发送自动通知。

以下是我们可以添加到架构示例的 Subscription 示例:

type Subscription { personAdded: Person }

每次将新的 Person 添加到数据源时,personAdded 字段都会向订阅的客户端发送消息。假设我们具有 personAdded 的解析器实施,我们现在可以使用订阅。虽然 Subscription 类型存在,但我们必须明确调用该类型,才能在应用程序的代码中运行。可以使用 subscription 关键字完成该操作:

subscription personAddedOperation { personAdded { id name } }

订阅命名为 personAddedOperation,操作为 personAddedpersonAdded 将返回新 Person 实例的 idname 字段。看一下变更示例,我们使用该操作添加了一个 Person

addPerson(id: "1", name: "Steve Powers", age: "50", occupation: "Miner")

如果我们的客户端订阅了新添加的 Person 的更新,它们可能会在 addPerson 运行后看到这一点:

{ "data": { "personAdded": { "id": "1", "name": "Steve Powers" } } }

以下是订阅提供的内容摘要:

订阅是双向通道,允许客户端和服务器接收快速但稳定的更新。它们通常使用 WebSocket 协议,该协议创建标准化的安全连接。

订阅非常灵活,因为它们减少了连接建立开销。在订阅后,客户端可以在较长时间内持续运行该订阅。它们通常允许开发人员定制订阅生命周期并配置请求哪些信息,从而高效地使用计算资源。

一般来说,订阅允许客户端一次进行多个订阅。由于与 Amazon AppSync 相关,订阅仅用于从 Amazon AppSync 服务接收实时更新。它们不能用于执行查询或变更。

订阅的主要替代方案是轮询,后者按设置的间隔发送查询以请求数据。该过程通常比订阅效率低,并且给客户端和后端带来很大压力。

我们的架构示例中没有提到的一件事是,还必须在 schema 根中定义您的特殊对象类型。因此,当您在 Amazon AppSync 中导出架构时,它可能如下所示:

schema.graphql
schema { query: Query mutation: Mutation subscription: Subscription } . . . type Query { # code goes here } type Mutation { # code goes here } type Subscription { # code goes here }

枚举

枚举是特殊标量,它限制类型或字段可能具有的合法参数。这意味着每次在架构中定义枚举时,其关联类型或字段仅限于枚举中的值。枚举被序列化为字符串标量。请注意,不同的编程语言可能以不同的方式处理 GraphQL 枚举。例如,JavaScript 没有本机枚举支持,因此,枚举值可能会映射到整数值。

枚举是使用 enum 关键字定义的。示例如下:

enum trafficSignals { solidRed solidYellow solidGreen greenArrowLeft ... }

在调用 trafficLights 枚举时,参数只能是 solidRedsolidYellowsolidGreen 等。通常使用枚举描述具有不同但有限数量的选项的内容。

联合/接口

请参阅 GraphQL 中的接口和联合