

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

# GraphQL 和架构 AWS AppSync
<a name="graphql-overview"></a>

**注意**  
本指南假设用户具有 REST 架构风格的实际经验。我们建议在使用 GraphQL 和 AWS AppSync之前查看该指南和其他前端主题。

GraphQL 是一种用于的查询和操作语言。 APIsGraphQL 提供了灵活直观的语法以描述数据要求和交互。它使开发人员能够准确询问所需的内容并获得可预测的结果。它还可以在单个请求中访问多个源，以减少网络调用次数和带宽要求，从而节省电池使用寿命和应用程序使用的 CPU 周期。

通过变更使数据更新变得更简单，以使开发人员能够描述数据应如何发生变化。GraphQL 还有助于通过订阅快速设置实时解决方案。所有这些功能相结合再加上强大的开发人员工具，使 GraphQL 在管理应用程序数据方面变得不可或缺。

GraphQL 是 REST 的替代方案。 RESTful 架构是目前比较流行的客户端-服务器通信解决方案之一。它以通过 URL 公开资源（数据）的概念为中心。它们 URLs 可用于通过 CRUD（创建、读取、更新、删除）操作访问和操作数据，采用 HTTP 方法（如`GET``POST`、和`DELETE`）。REST 的优点是学习和实施相对简单。您可以快速设置 RESTful APIs 为呼叫各种服务。

不过，技术正变得越来越复杂。随着应用程序、工具和服务开始扩展到全球受众，对快速且可扩展的架构的需求变得至关重要。REST 在处理可扩展的操作时存在很多缺点。有关示例，请参阅该[使用案例](https://aws.amazon.com/blogs/architecture/what-to-consider-when-modernizing-apis-with-graphql-on-aws/)。

在以下各节中，我们将回顾一些与之相关的概念 RESTful APIs。然后，我们介绍 GraphQL 及其工作方式。

有关 GraphQL 以及迁移到的好处的更多信息 AWS，请参阅 [GraphQL 实现决策指南](https://aws.amazon.com/graphql/guide/)。

**Topics**
+ [什么是 API](what-is-an-api.md)
+ [什么是 REST](what-is-rest.md)
+ [什么是 GraphQL](what-is-graphql.md)
+ [比较 REST 和 GraphQL](comparing-rest-graphql.md)
+ [为什么使用 GraphQL 而不是 REST](why-use-graphql.md)
+ [GraphQL API 组件](api-components.md)
+ [GraphQL 的其他属性](graphql-properties.md)

# 什么是 API？
<a name="what-is-an-api"></a>

应用程序编程接口 (API) 定义与其他软件系统通信时必须遵循的规则。开发人员公开或创建， APIs 以便其他应用程序可以通过编程方式与其应用程序通信。例如，工时表应用程序公开一个 API，要求提供员工的全名和日期范围。在它收到该信息时，它在内部处理员工的工时表，并返回该日期范围内的工作小时数。

您可以将 Web API 视为客户端和 Web 上的资源之间的网关。

## 客户端
<a name="what-is-a-client"></a>

客户端是希望从 Web 中访问信息的用户。客户端可以是一个人，也可以是一个使用 API 的软件系统。例如，开发人员可以编写程序，从天气系统中访问天气数据。或者，在您直接访问天气网站时，您可以从浏览器中访问相同的数据。

## 资源
<a name="what-is-a-resource"></a>

资源是不同应用程序向其客户端提供的信息。资源可以是图像、视频、文本、数字或任何类型的数据。向客户端提供资源的计算机也称为服务器。Org APIs anizations 用于共享资源和提供 Web 服务，同时维护安全、控制和身份验证。此外， APIs 帮助他们确定哪些客户可以访问特定的内部资源。

# 什么是 REST？
<a name="what-is-rest"></a>

概括地说，表述性状态传输 (REST) 是一种软件架构，它对 API 的工作方式施加了条件。REST 最初是作为管理互联网等复杂网络上的通信的准则创建的。您可以使用基于 REST 的架构支持大规模的高性能且可靠的通信。您可以轻松进行实施和修改，从而为任何 API 系统提供可见性和跨平台可移植性。

API 开发人员 APIs 可以使用几种不同的架构进行设计。 APIs 遵循 REST 架构风格的被称为 REST APIs。实现 REST 架构的 Web 服务被称为 RESTful Web 服务。 RESTful API 一词通常指 RESTful 网络 APIs。但是，您可以互换使用 REST API 和 RESTful API 这两个术语。

以下是 REST 架构风格的一些准则：

## 统一接口
<a name="uniform-interface"></a>

统一接口是设计任何 RESTful Web 服务的基础。它表示服务器以标准格式传输信息。设置了格式的资源在 REST 中称为表示形式。该格式可能与服务器应用程序上的资源的内部表示形式不同。例如，服务器可以将数据存储为文本，但以 HTML 表示形式发送。

统一接口施加了 4 个架构限制：

1.  请求应标识资源。它们使用统一资源标识符以实现该目的。

1.  客户端在资源表示形式中具有足够多的信息，可以根据需要修改或删除资源。服务器发送进一步描述资源的元数据以满足该条件。

1.  客户端收到有关如何进一步处理表示形式的信息。服务器发送自描述消息以实现该目的，这些消息包含有关客户端如何以最佳方式使用它们的元数据。

1.  客户端收到完成任务所需的所有其他相关资源的信息。服务器在表示形式中发送超链接以实现该目的，以使客户端可以动态发现更多资源。

## 无状态
<a name="statelessness"></a>

在 REST 架构中，无状态是指服务器独立于所有以前请求完成每个客户端请求的通信方法。客户端可以按任意顺序请求资源，并且每个请求都是无状态或与其他请求隔离。这种 REST API 设计限制意味着，服务器每次都能完全了解并完成请求。

## 分层系统
<a name="layered-system"></a>

在分层系统架构中，客户端可以连接到客户端和服务器之间的其他授权中间设备，并且仍然会接收来自服务器的响应。服务器也可以将请求传送到其他服务器。您可以将 RESTful Web 服务设计为在具有多层（例如安全、应用程序和业务逻辑）的多台服务器上运行，它们协同工作以满足客户请求。这些层对客户端是不可见的。

## 可缓存性
<a name="cacheability"></a>

RESTful Web 服务支持缓存，即在客户端或中间存储一些响应以缩短服务器响应时间的过程。例如，假设您访问的网站的每个页面上具有通用的页眉和页脚图像。每次您访问新的网站页面时，服务器都必须重新发送相同的图像。为避免这种情况，客户端会在第一次响应后缓存或存储这些图像，然后直接使用缓存中的图像。 RESTful Web 服务通过使用将自己定义为可缓存或不可缓存的 API 响应来控制缓存。

## 什么是 RESTful API？
<a name="what-is-a-restful-api"></a>

RESTful API 是两个计算机系统用来通过互联网安全交换信息的接口。大多数业务应用程序必须与其他内部和第三方应用程序通信以执行各种任务。例如，要生成月度工资单，您的内部账户系统必须与客户的银行系统共享数据，以自动开具发票，并与内部工时表应用程序进行通信。 RESTful APIs 支持这种信息交换，因为它们遵循安全、可靠和高效的软件通信标准。

## 如何 RESTful APIs 运作？
<a name="how-do-restful-apis-work"></a>

 RESTful API 的基本功能与浏览互联网相同。在客户端需要资源时，它使用 API 连接到服务器。API 开发人员在服务器应用程序 API 文档中解释了客户端应如何使用 REST API。以下是任何 REST API 调用的一般步骤：

1.  客户端向服务器发送请求。客户端按照 API 文档以服务器理解的方式设置请求格式。

1.  服务器对客户端进行身份验证，并确认客户端有权发出该请求。

1.  服务器接收该请求，并在内部进行处理。

1.  服务器向客户端返回响应。响应包含告诉客户端请求是否成功的信息。响应还包含客户端请求的任何信息。

根据 API 开发人员设计 API 的方式，REST API 请求和响应详细信息略有不同。

# 什么是 GraphQL？
<a name="what-is-graphql"></a>

GraphQL 既是用于执行这些查询的查询语言， APIs 又是执行这些查询的运行时。GraphQL 允许客户请求所需的确切数据，从而在许多场景中提供一种更灵活且更高效的 REST 替代方案。与依赖预定义端点的 REST 不同，GraphQL 使用单个端点，客户可在其中以查询和变更的形式指定其数据要求。

有关 [GraphQL 结构的更多信息，请参阅 GraphQL API 的组件](https://docs.aws.amazon.com/appsync/latest/devguide/api-components.html)。 APIs 

# 比较 REST 和 GraphQL
<a name="comparing-rest-graphql"></a>

APIs （应用程序编程接口）在促进应用程序之间的数据交换方面起着至关重要的作用。如前所述，出现 APIs 了两种突出的设计方法：GraphQL和REST。虽然两者的基本目的都是实现客户端-服务器通信，但它们的实施和使用案例有很大不同。

GraphQL 和 REST 有几个共同的关键特征：

1. **客户端-服务器模型**：两者都使用客户端-服务器架构进行数据交换。

1. **无状态**：两者都不会在两次请求之间保留客户端会话信息。

1. **基于 HTTP**：两者通常都使用 HTTP 作为底层通信协议。

1. **以资源为导向的设计**：两者都围绕资源来设计数据交换，资源是指客户端可通过 API 访问和操作的任何数据或对象。

1. **数据格式灵活性**：JSON 是两者中最常用的数据交换格式，但也支持 XML 和 HTML 等其他格式。

1. **与语言和数据库无关**：两者都可以使用任何编程语言或数据库结构，因此具有很高的互操作性。

1. **缓存支持**：两者都支持缓存，允许客户端和服务器存储经常访问的数据以提高性能。

GraphQL 和 REST 虽然具有一些共同的基本原则，但在 API 设计和数据获取方法上有很大的不同：

1. **请求结构和数据获取**

   REST 使用不同的 HTTP 方法（GET、POST、PUT、DELETE）对资源执行操作。这通常需要使用多个端点处理不同的资源，从而导致数据检索效率低下。例如，运行 GET 操作来检索用户数据可能会导致数据过度获取或获取不足。为获取正确的数据，可能会调用截断操作或调用多个操作。

   GraphQL 使用单个端点执行所有操作。它依赖查询来获取数据，依赖变更来修改数据。客户端可以使用查询在单个请求中精确获取所需的数据，从而通过更大限度地减少数据传输来减少网络开销。

1. **服务器端架构**

   REST 不需要服务器端架构，但可以选择定义服务器端架构，以实现高效的 API 设计和文档。

   GraphQL 使用强类型服务器端架构来定义数据和数据服务。该架构采用 GraphQL 架构定义语言（SDL）编写，包括每个对象的对象类型和字段，以及为每个字段定义操作的服务器端解析器函数。

1. **版本控制**

   REST 通常在 URL 中包含版本控制，这可能会导致同时维护多个 API 版本。版本控制不是强制性的，但可以帮助防止重大更改。

   GraphQL 要求向后兼容，从而在不进行明确版本控制的情况下促进 API 的持续发展。删除的字段会返回错误消息，而弃用标签会逐步淘汰旧字段并返回警告消息。

1. **错误处理** 

   REST 为弱类型，需要在周围的代码中内置错误处理。这可能无法自动识别与类型相关的错误（例如，将数字解析为文本）。

   相比之下，GraphQL 为强类型，需要全面的架构定义。这使您的服务能够自动识别许多请求错误且详细程度较高。

1. **使用案例**

   REST 更适合：
   + 数据要求不那么复杂的较小应用程序。
   + 所有客户端以类似方式使用数据和操作的场景。
   + 没有复杂数据查询需求的应用程序。

   GraphQL 更适合：
   + 带宽有限的场景，其中更大限度减少请求和响应至关重要。
   + 具有多个数据来源的应用程序，需要在单个端点处合并这些数据来源。
   + 客户端请求差异很大并且预计响应结构会有所不同的情况。

   请注意，可以在单个应用程序 APIs 中同时使用 GraphQL 和 REST 来实现不同的功能领域。此外，您无需完全重写即可升级 RESTful API 以包含 GraphQL 功能。有关示例，请参阅[如何为 AWS 数据源构建 GraphQL 解析器](https://aws.amazon.com/graphql/resolvers/)。

# 为什么使用 GraphQL 而不是 REST？
<a name="why-use-graphql"></a>

REST 是 Web 的基石架构风格之一 APIs。不过，随着世界的互联程度变得越来越高，开发稳健且可扩展的应用程序的需求成为更紧迫的问题。虽然 REST 通常用于构建 Web APIs，但已经发现了 RESTful 实现中反复出现的几个缺点：

1. **数据请求**：使用 RESTful APIs，您通常会通过端点请求所需的数据。在您的数据可能没有那么整齐打包时，就会出现问题。您需要的数据可能位于多个抽象层后面，获取数据的唯一方法是使用多个终端节点，这意味着发出多个请求以获取所有数据。

1. **过度获取和获取不足**：除了多个请求的问题以外，来自每个终端节点的数据都是严格定义的，这意味着您返回为该 API 定义的任何数据，即使您在技术上并不需要这些数据。

   这可能会导致*过度获取*，这意味着我们的请求返回多余的数据。例如，假设您请求公司个人数据，并希望知道某个部门的员工姓名。返回数据的终端节点将包含姓名，但也可能包含其他数据，例如职位或出生日期。由于 API 是固定的，因此，您无法只请求姓名本身；其余数据也会随之而来。

   相反的情况是，我们没有返回足够的数据，这称为*获取不足*。要获取请求的所有数据，您可能需要向服务发出多个请求。根据设置数据结构的方式，您可能会遇到效率低下的查询，从而导致类似于令人头痛的 n\$11 问题的情况。

1. **开发迭代缓慢**：许多开发人员 RESTful APIs 根据其应用程序的流程对其进行定制。不过，随着应用程序的扩展，前端和后端可能需要进行大量更改。因此， APIs 可能不再以高效或有影响力的方式适应数据的形状。由于需要修改 API，这会导致产品迭代速度变慢。

1. **大规模性能**：由于这些复杂的问题，很多领域的可扩展性都会受到影响。应用程序端的性能可能会受到影响，因为您的请求返回太多的数据或太少的数据（导致更多请求）。这两种情况都会对网络造成不必要的压力，从而导致性能下降。在开发人员方面，开发速度可能会降低，因为您的数据 APIs 是固定的，不再适合他们要求的数据。

GraphQL 的卖点是克服 REST 的缺点。以下是 GraphQL 为开发人员提供的一些关键解决方案：

1. **单个终端节点**：GraphQL 使用单个终端节点查询数据。无需构建多个 APIs 以适应数据的形状。这会导致通过网络传输的请求减少。

1. **获取**：GraphQL 直接定义所需的数据以解决长期存在的过度获取和获取不足问题。GraphQL 允许您设置数据形状以满足您的需求，从而仅收到所要求的内容。

1. **摘要**：G APIs raphQL 包含一些组件和系统，它们使用与语言无关的标准来描述数据。换句话说，数据形状和结构是标准化的，因此，前端和后端知道如何通过网络发送数据。这使两端的开发人员可以使用 GraphQL 的系统，而不是围绕它们工作。

1. **快速迭代**：由于数据标准化，在开发的一端进行更改时，可能不需要在另一端进行更改。例如，前端表示形式更改可能不会导致在后端进行大量更改，因为 GraphQL 可以轻松修改数据规范。您可以直接定义或修改数据的形状，以满足应用程序扩展时的需求。这会导致潜在的开发工作减少。

这些只是 GraphQL 的一部分好处。在接下来的几节中，您将了解如何设置 GraphQL 结构以及使其成为 REST 的独特替代方案的属性。

# GraphQL API 组件
<a name="api-components"></a>

标准 GraphQL API 由单个架构组成，该架构处理查询的数据的形状。您的架构链接到一个或多个数据来源，例如数据库或 Lambda 函数。在它们之间具有一个或多个解析器，用于处理您的请求的业务逻辑。每个组件在 GraphQL 实施中都发挥着重要作用。以下几节介绍了这三个组件以及它们在 GraphQL 服务中发挥的作用。

![\[GraphQL API components: schema, resolvers, and data sources interconnected with AppSync.\]](http://docs.aws.amazon.com/zh_cn/appsync/latest/devguide/images/appsync-architecture-graphql-api.png)


**Topics**
+ [GraphQL 架构](schema-components.md)
+ [数据来源](data-source-components.md)
+ [解析器](resolver-components.md)

# GraphQL 架构
<a name="schema-components"></a>

GraphQL 架构是 GraphQL API 的基础。它充当定义数据形状的蓝图。它也是您的客户端和服务器之间的合同，它定义了如何检索和 and/or 修改您的数据。

GraphQL 架构是使用*架构定义语言* (SDL) 编写的。SDL 由具有既定结构的类型和字段组成：
+ **类型**：类型是 GraphQL 定义数据形状和行为的方式。GraphQL 支持多种类型，本节后面将介绍这些类型。架构中定义的每种类型将包含自己的范围。在该范围内具有一个或多个字段，这些字段可以包含在 GraphQL 服务中使用的值或逻辑。类型扮演很多不同的角色，最常见的角色是对象或标量（基元值类型）。
+ **字段**：字段位于类型范围内，并保存从 GraphQL 服务中请求的值。它们与其他编程语言中的变量非常相似。您在字段中定义的数据形状将决定 request/response 操作中数据的结构方式。这样，开发人员就可以在不知道服务后端实施方式的情况下预测返回的内容。

为了直观地了解架构的外观，让我们看一个简单 GraphQL 架构的内容。在生产代码中，您的架构通常位于名为 `schema.graphql` 或 `schema.json` 的文件中。假设我们正在研究一个实施 GraphQL 服务的项目。该项目存储公司人员数据，并使用 `schema.graphql` 文件检索人员数据以及在数据库中添加新人员。代码可能如下所示：

------
#### [ schema.graphql ]

```
type Person {                                  
   id: ID!
   name: String                                  
   age: Int
}
type Query {                                   
  people: [Person]
}
type Mutation {
  addPerson(id: ID!, name: String, age: Int): Person
}
```

------

我们可以看到在架构中定义了三种类型：`Person`、`Query` 和 `Mutation`。看一下 `Person`，我们可以猜到这是公司员工实例的蓝图，这会使该类型成为一个对象。在其范围内，我们看到 `id`、`name` 和 `age`。这些是定义 `Person` 属性的字段。这意味着我们的数据来源将每个 `Person` 的 `name` 存储为 `String` 标量（基元）类型，并将 `age` 存储为 `Int` 标量（基元）类型。`id` 充当每个 `Person` 的特殊唯一标识符。它也是一个必需的值，由 `!` 符号表示。

接下来的两个对象类型的行为有所不同。GraphQL 为特殊对象类型保留一些关键字，这些关键字定义如何在架构中填充数据。`Query` 类型从源中检索数据。在我们的示例中，我们的查询可能从数据库中检索 `Person` 对象。这可能会让你想起 RESTful 术语中的`GET`操作。`Mutation` 修改数据。在我们的示例中，我们的变更可能会在数据库中添加更多 `Person` 对象。这可能会让您想起 `PUT` 或 `POST` 等状态更改操作。本节稍后将介绍所有特殊对象类型的行为。

让我们假设示例中的 `Query` 从数据库中检索一些内容。如果我们查看 `Query` 的字段，就会看到一个名为 `people` 的字段。其字段值为 `[Person]`。这意味着我们要检索数据库中的某个 `Person` 实例。不过，添加方括号意味着，我们希望返回所有 `Person` 实例的列表，而不仅仅是一个特定的实例。

`Mutation` 类型负责执行状态更改操作，例如数据修改。变更负责对数据来源执行一些状态更改操作。在我们的示例中，变更包含一个名为 `addPerson` 的操作，它将新的 `Person` 对象添加到数据库中。该变更使用 `Person` 并需要使用 `id`、`name` 和 `age` 字段的输入。

此时，您可能想知道 `addPerson` 等操作在没有代码实施的情况下如何工作，因为它应该实施某种行为，并且看起来很像具有函数名称和参数的函数。目前，它不起作用，因为架构仅充当声明。要实施 `addPerson` 行为，我们必须为其添加一个解析器。解析器是一个代码单元，只要调用其关联字段（此处的 `addPerson` 操作），就会执行该代码单元。如果要使用某个操作，您必须在某个时候添加解析器实施。从某种意义上说，您可以将架构操作视为函数声明，并将解析器视为定义。将在另一个小节中介绍解析器。

该示例仅显示架构处理数据的最简单方法。您可以使用 GraphQL 和 AWS AppSync功能构建复杂、稳健且可扩展的应用程序。在下一节中，我们将定义您可以在架构中使用的所有不同类型和字段行为。

# GraphQL 类型
<a name="graphql-types"></a>

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

类型可以分为输入和输出。输入是允许作为特殊对象类型（`Query`、`Mutation` 等）的参数传入的类型，而输出类型严格用于存储和返回数据。下面列出了类型及其分类的列表：
+ **对象**：对象包含描述实体的字段。例如，一个对象可能类似于 `book`，其中包含描述其特性的字段，如 `authorName`、`publishingYear` 等。它们严格来说是输出类型。
+ **标量**：这些是基元类型，如整数、字符串等。它们通常分配给字段。以 `authorName` 字段为例，可以为其分配 `String` 标量以存储名称，例如“John Smith”。标量可以是输入类型和输出类型。
+ **输入**：输入允许您传递一组字段以作为参数。它们的结构与对象非常相似，但可以将其作为特殊对象的参数传递。输入允许您在其范围内定义标量、枚举和其他输入。输入只能是输入类型。
+ **特殊对象**：特殊对象执行状态更改操作，并完成服务的大部分繁重工作。共有三种特殊对象类型：查询、变更和订阅。查询通常获取数据；变更处理数据；订阅在客户端和服务器之间打开并保持双向连接以进行持续通信。鉴于其功能，特殊对象既不是输入，也不是输出。
+ **枚举**：枚举是预定义的合法值列表。如果调用枚举，则枚举值只能是其范围内定义的值。例如，如果您使用一个名为 `trafficLights` 的枚举以描述交通信号列表，它可能具有 `redLight` 和 `greenLight` 等值，但不能具有 `purpleLight` 值。真正的交通信号灯只有那么多信号，因此，您可以使用枚举定义它们，并在引用 `trafficLight` 时强制使它们成为唯一的合法值。枚举可以是输入类型和输出类型。
+ **联合/接口**：联合允许您根据客户端请求的数据在请求中返回一个或多个内容。例如，如果您使用具有 `title` 字段的 `Book` 类型以及具有 `name` 字段的 `Author` 类型，您可以在这两种类型之间创建联合。如果您的客户端希望在数据库中查询“Julius Caesar”，则联合可能会从 `Book` `title` 返回 *Julius Caesar*（威廉·莎士比亚的戏剧），并从 `Author` `name` 返回 *Julius Caesar*（*Commentarii de Bello Gallico* 的作者）。联合只能是输出类型。

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

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

## 对象
<a name="object-components"></a>

GraphQL 对象是您在生产代码中看到的主要类型。在 GraphQL 中，您可以将对象视为不同字段的分组（类似于其他语言中的变量），每个字段由可以保存值的类型（通常是标量或另一个对象）定义。对象表示可以 retrieved/manipulated 来自您的服务实现的数据单元。

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

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

type Occupation {
  title: String
}
```

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

## 标量
<a name="scalar-components"></a>

标量本质上是保存值的基元类型。在中 AWS AppSync，有两种类型的标量：默认的 GraphQL 标量和标量 AWS AppSync 。标量通常用于存储对象类型中的字段值。默认 GraphQL 类型包括 `Int`、`Float`、`String`、`Boolean` 和 `ID`。让我们再次使用上一示例：

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

type Occupation {
  title: String
}
```

挑出 `name` 和 `title` 字段，两者都包含 `String` 标量。`Name` 可能返回类似于 "`John Smith`" 的字符串值，title 可能返回类似于 "`firefighter`" 的值。一些 GraphQL 实施还支持使用 `Scalar` 关键字并实施类型行为的自定义标量。不过， AWS AppSync 目前**不支持**自定义标量。有关标量的列表，请参阅 [AWS AppSync中的标量类型](https://docs.aws.amazon.com//appsync/latest/devguide/scalars.html)。

## 输入
<a name="input-components"></a>

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

输入是使用 `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 架构的很好做法是，仅定义需要作为参数输入的内容。

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

## 特殊对象
<a name="special-object-components"></a>

GraphQL 为特殊对象保留了一些关键字，这些对象定义了架构如何处理数据的一些业务逻辑。 retrieve/manipulate 最多可以在架构中包含其中的关键字之一。它们充当客户端对 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 中，我们可以将查询视为单一请求来源。如您所见，这可能比可能使用不同端点来 RESTful 实现相同目标（`.../api/1/people`和`.../api/1/occupations`）的实现要简单得多。

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

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

正如您看到的一样，该查询命名为 `getItems` 并返回 `people`（`Person` 对象列表）和 `occupations`（`Occupation` 对象列表）。在 `people` 中，我们仅返回每个 `Person` 的 `name` 字段，同时返回每个 `Occupation` 的 `title` 字段。响应可能如下所示：

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

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

```
query getItems {
   people {
      name   
      occupation {
        title
      }
}
```

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

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

------
#### [ Mutations ]

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

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

`addPerson` 字段表示将 `Person` 添加到数据来源的一个入口点。`addPerson` 是字段名称；`id`、`name` 和 `age` 是参数；`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
}
```

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

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

该变更命名为 `createPerson`，`addPerson` 是操作。要创建新的 `Person`，我们可以输入 `id`、`name`、`age` 和 `occupation` 的参数。在 `addPerson` 的范围内，我们还可以看到其他字段，如 `name`、`age` 等。这是您的响应；这些是 `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](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_client_applications)阅用于在服务器与其客户端之间建立持久的双向连接。通常，客户端订阅或侦听服务器。每次服务器进行服务器端更改或执行事件时，订阅的客户端都会收到更新。如果订阅了多个客户端，并且需要向它们通知服务器或其他客户端中发生的更改，这种类型的协议是非常有用的。例如，可以使用订阅更新社交媒体源。可能具有两个用户（用户 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`，操作为 `personAdded`。`personAdded` 将返回新 `Person` 实例的 `id` 和 `name` 字段。看一下变更示例，我们使用该操作添加了一个 `Person`：

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

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

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

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

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

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

一般来说，订阅允许客户端一次进行多个订阅。实际上 AWS AppSync，订阅仅用于接收来自该 AWS AppSync 服务的实时更新。它们不能用于执行查询或变更。

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

------

我们的架构示例中没有提到的一件事是，还必须在 `schema` 根中定义您的特殊对象类型。因此，当你在中导出架构时 AWS 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
}
```

------

## 枚举
<a name="enum-components"></a>

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

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

```
enum trafficSignals {
  solidRed
  solidYellow
  solidGreen
  greenArrowLeft
  ...
}
```

在调用 `trafficLights` 枚举时，参数只能是 `solidRed`、`solidYellow`、`solidGreen` 等。通常使用枚举描述具有不同但有限数量的选项的内容。

## 联合/接口
<a name="union-interface-components"></a>

请参阅 GraphQL 中的[接口和联合](https://docs.aws.amazon.com/appsync/latest/devguide/interfaces-and-unions.html)。

# GraphQL 字段
<a name="graphql-fields"></a>

字段位于类型范围内，并保存从 GraphQL 服务中请求的值。它们与其他编程语言中的变量非常相似。例如，以下是一个 `Person` 对象类型：

```
type Person {                                  
   name: String                                  
   age: Int
}
```

此处的字段为 `name` 和 `age`，分别保存 `String` 和 `Int` 值。可以将像上面所示的对象字段作为查询和变更字段（操作）中的输入。例如，请参阅下面的 `Query`：

```
type Query {                                   
  people: [Person]
}
```

`people` 字段从数据来源中请求所有 `Person` 实例。在 GraphQL 服务器中添加或检索 `Person` 时，您可以要求数据采用您的类型和字段的格式，即，架构中的数据结构决定了如何在响应中设置其结构：

```
}
  "data": {
    "people": [
      {
        "name": "John Smith",
        "age": "50"
      },
      {
        "name": "Andrew Miller",
        "age": "60"
      },
      .
      .
      .
    ]
  }
}
```

字段在设置数据结构方面发挥着重要作用。下面介绍了几个额外的属性，可以将其应用于字段以进行更多自定义。

## Lists
<a name="list-components"></a>

列表返回指定类型的所有项目。可以使用方括号 `[]` 将列表添加到字段类型中：

```
type Person { 
  name: String
  age: Int
}
type Query {                                   
  people: [Person]
}
```

在 `Query` 中，`Person` 两侧的方括号表示您希望以数组形式从数据来源返回所有 `Person` 实例。在响应中，每个 `Person` 的 `name` 和 `age` 值作为单个分隔列表返回：

```
}
  "data": {
    "people": [
      {
        "name": "John Smith",         # Data of Person 1
        "age": "50"
      },
      {
        "name": "Andrew Miller",      # Data of Person 2
        "age": "60"
      },
      .                               # Data of Person N
      .
      .
    ]
  }
}
```

您不限于使用特殊对象类型。您还可以在常规对象类型的字段中使用列表。

## 非 Null 值
<a name="non-null-components"></a>

非 Null 值表示在响应中不能为 Null 的字段。您可以使用 `!` 符号将字段设置为非 Null：

```
type Person { 
  name: String!
  age: Int
}
type Query {                                   
  people: [Person]
}
```

`name` 字段不能明确为 Null。如果您要查询数据来源并为该字段提供 Null 输入，则会引发错误。

您可以组合使用列表和非 Null 值。比较以下查询：

```
type Query {                                   
  people: [Person!]      # Use case 1
}

.
.
.

type Query {                                   
  people: [Person]!      # Use case 2
}

.
.
.

type Query {                                   
  people: [Person!]!     # Use case 3
}
```

在使用案例 1 中，列表不能包含 Null 项目。在使用案例 2 中，列表本身不能设置为 Null。在使用案例 3 中，列表及其项目不能为 Null。不过，在任何案例中，您仍然可能会返回空列表。

正如您看到的一样，在 GraphQL 中具有很多不断变化的组件。在本节中，我们说明了一个简单架构的结构以及架构支持的各种类型和字段。在下一节中，您将了解 GraphQL API 的其他组件以及它们如何与架构一起使用。

# 数据来源
<a name="data-source-components"></a>

在上一节中，我们了解了架构定义数据的形状。不过，我们从未介绍这些数据来自哪里。在实际项目中，您的架构就像一个网关，它处理向服务器发出的所有请求。在发出请求时，架构充当与客户端交互的单个终端节点。架构访问和处理数据，并将其从数据来源转发回客户端。请参阅下面的信息图：

![\[GraphQL schema integrating multiple AWS 服务 for a single endpoint API architecture.\]](http://docs.aws.amazon.com/zh_cn/appsync/latest/devguide/images/aws-flow-infographic.png)


AWS AppSync 而且 GraphQL 出色地实现了前端后端 (BFF) 解决方案。它们协同工作，通过抽象后端来降低大规模系统的复杂性。如果您的服务使用不同的数据来源和/或微服务，您实际上可以在单个架构（超图）中定义每个源（子图）的数据形状以抽象掉一些复杂性。这意味着您的 GraphQL API 不限于使用一个数据来源。您可以将任意数量的数据来源与 GraphQL API 关联，并在代码中指定它们如何与服务进行交互。

正如您在信息图中看到的一样，GraphQL 架构包含客户端请求数据所需的所有信息。这意味着可以在单个请求中处理所有内容，而不是像 REST 那样在多个请求中进行处理。这些请求经过架构，这是服务的唯一终端节点。在处理请求时，解析器（在下一节中介绍）执行其代码以处理来自相关数据来源的数据。在返回响应时，将使用架构中的数据填充与数据来源关联的子图。

AWS AppSync 支持许多不同的数据源类型。在下表中，我们将描述每种类型，列出每种类型的一些优点，并提供非常有用的链接以获取额外的背景信息。


| 数据来源 | 说明 | 优势 | 补充信息 | 
| --- | --- | --- | --- | 
| Amazon DynamoDB | “Amazon DynamoDB 是一种完全托管的 NoSQL 数据库服务，可以提供快速且可预测的性能以及无缝可扩展性。DynamoDB 可以免除操作和扩展分布式数据库的管理工作负担，因而无需担心硬件预置、设置和预置、复制、软件修补或集群扩展等问题。DynamoDB 还提供静态加密，从而消除了保护敏感数据时涉及的运行负担和复杂性。” |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/appsync/latest/devguide/data-source-components.html)  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/appsync/latest/devguide/data-source-components.html)  | 
| AWS Lambda | “AWS Lambda 是一项计算服务，可让您在不预配置或管理服务器的情况下运行代码。Lambda 在可用性高的计算基础设施上运行您的代码，执行计算资源的所有管理工作，其中包括服务器和操作系统维护、容量预置和弹性伸缩和记录。在使用 Lambda 时，您只需在 Lambda 支持的语言运行时系统之一中提供代码。” |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/appsync/latest/devguide/data-source-components.html)  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/appsync/latest/devguide/data-source-components.html)  | 
| OpenSearch | “Amazon OpenSearch Service 是一项托管服务，可以轻松地在 AWS 云中部署、操作和扩展 OpenSearch 集群。亚马逊 OpenSearch 服务支持 OpenSearch 传统的 Elasticsearch OSS（最高 7.10，即该软件的最终开源版本）。创建集群时，您可以选择使用哪种搜索引擎。**OpenSearch**是一个完全开源的搜索和分析引擎，用于日志分析、实时应用程序监控和点击流分析等用例。有关详情，请参阅 [OpenSearch 文档](https://opensearch.org/docs/)。**Amazon S OpenSearch ervic** e 会为您的 OpenSearch 集群配置所有资源并启动它。它还可以自动检测和替换出现故障的 OpenSearch 服务节点，从而减少与自我管理基础架构相关的开销。您只需调用一次 API 或在控制台中单击几下鼠标按钮，即可扩展集群。” |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/appsync/latest/devguide/data-source-components.html)  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/appsync/latest/devguide/data-source-components.html)  | 
| HTTP 端点 | 您可以使用 HTTP 端点作为数据源。 AWS AppSync 可以向端点发送包含参数和有效载荷等相关信息的请求。将向解析器公开 HTTP 响应，解析器在完成操作后返回最终响应。 |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/appsync/latest/devguide/data-source-components.html)  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/appsync/latest/devguide/data-source-components.html)  | 
| Amazon EventBridge | “EventBridge 是一项无服务器服务，它使用事件将应用程序组件连接在一起，使您可以更轻松地构建可扩展的事件驱动应用程序。使用它可以将来自本土应用程序、 AWS 服务和第三方软件等来源的事件路由到组织中的消费者应用程序。 EventBridge 提供了一种简单而一致的方法来提取、筛选、转换和交付事件，因此您可以快速构建新应用程序。” |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/appsync/latest/devguide/data-source-components.html)  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/appsync/latest/devguide/data-source-components.html)  | 
| 关系数据库 | “Amazon Relational Database Service（Amazon RDS）是一项网络服务，可以更轻松地在 AWS 云中设置、操作和扩展关系数据库。它为符合行业标准的关系数据库提供经济高效且可调整大小的容量，并管理常见的数据库管理任务。” |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/appsync/latest/devguide/data-source-components.html)  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/appsync/latest/devguide/data-source-components.html)  | 
| None 数据来源 | 如果不打算使用数据来源服务，您可以将其设置为 none。虽然 none 数据来源仍明确归类为数据来源，但并不是存储介质。尽管如此，它在某些情况下对于数据处理和传递仍然非常有用。 |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/appsync/latest/devguide/data-source-components.html)  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/appsync/latest/devguide/data-source-components.html)  | 

**提示**  
有关数据源如何与之交互的更多信息 AWS AppSync，请参阅[附加数据源](https://docs.aws.amazon.com//appsync/latest/devguide/attaching-a-data-source.html)。

# 解析器
<a name="resolver-components"></a>

从前面的几节中，您了解了架构和数据来源的组件。现在，我们需要解决架构和数据来源如何交互的问题。这一切都始于解析器。

解析器是一个代码单元，可以处理向服务发出请求时如何解析该字段的数据的问题。解析器附加到架构中的您的类型内的特定字段。它们通常用于实施查询、变更和订阅字段操作的状态更改操作。解析器处理客户端的请求，然后返回结果，结果可能是一组输出类型，例如对象或标量：

![\[GraphQL schema with resolvers connecting to various AWS data sources for a single endpoint.\]](http://docs.aws.amazon.com/zh_cn/appsync/latest/devguide/images/aws-flow-infographic.png)


## 解析器运行时系统
<a name="resolver-components-runtime"></a>

在中 AWS AppSync，必须先为解析器指定运行时间。解析器运行时系统表示执行解析器的环境。它还决定了你的解析器将使用的语言。 AWS AppSync 目前支持 APPSYNC\$1JS JavaScript 和 Velocity 模板语言 (VTL)。参见[解析器的JavaScript 运行时功能和函数](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-util-reference-js.html) JavaScript 或 VTL 的[解析器映射模板实用程序参考](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-util-reference.html)。

## 解析器结构
<a name="resolver-components-structure"></a>

从代码角度看，可以通过多种方式设置解析器结构。具有**单位**解析器和**管道**解析器。

### 单位解析器
<a name="resolver-components-unit"></a>

单位解析器由定义对数据来源执行的单个请求和响应处理程序的代码组成。请求处理程序将上下文对象作为参数，并返回用于调用数据来源的请求负载。响应处理程序接收从数据来源返回的负载以及执行的请求结果。响应处理程序将负载转换为 GraphQL 响应以解析 GraphQL 字段。

![\[GraphQL request flow showing request and response handlers interacting with a data source.\]](http://docs.aws.amazon.com/zh_cn/appsync/latest/devguide/images/unit-resolver-js.png)


### 管道解析器
<a name="resolver-components-pipeline"></a>

在实施管道解析器时，它们采用通用的结构：
+ **预备步骤**：在客户端发出请求时，将为使用的架构字段（通常是查询、变更、订阅）的解析器传送请求的数据。解析器开始使用预备步骤处理程序处理请求数据，该处理程序允许在数据传送到解析器之前执行一些预处理操作。
+ **函数**：在运行预备步骤后，请求传送到函数列表。将对数据来源执行列表中的第一个函数。函数是解析器代码的子集，其中包含自己的请求和响应处理程序。请求处理程序获取请求数据，并对数据来源执行操作。在将数据来源的响应传回到列表之前，响应处理程序对其进行处理。如果具有多个函数，请求数据将发送到列表中的下一个函数以进行执行。列表中的函数按照开发人员定义的顺序依次执行。在执行所有函数后，最终结果传送到后续步骤。
+ **后续步骤**：后续步骤是一个处理程序函数，允许您在将最终函数的响应传送到 GraphQL 响应之前对其执行一些最终操作。

![\[GraphQL request flow diagram showing interactions between request, data sources, and response components.\]](http://docs.aws.amazon.com/zh_cn/appsync/latest/devguide/images/appsync-js-resolver-logic.png)


## 解析器处理程序结构
<a name="resolver-components-handlers"></a>

处理程序通常是名为 `Request` 和 `Response` 的函数：

```
export function request(ctx) {
    // Code goes here
}

export function response(ctx) {
    // Code goes here
}
```

在单位解析器中，只有一组这样的函数。在管道解析器中，预备步骤和后续步骤具有一组这样的函数，并为每个函数额外提供一组这样的函数。为了直观地了解它的外观，让我们看一个简单的 `Query` 类型：

```
type Query {
	helloWorld: String!
}
```

这是一个简单的查询，其中包含一个名为 `helloWorld` 且类型为 `String` 的字段。假设我们始终希望该字段返回字符串“Hello World”。为了实现该行为，我们需要将解析器添加到该字段中。在单位解析器中，我们可以添加如下内容：

```
export function request(ctx) {
    return {}
}

export function response(ctx) {
    return "Hello World"
}
```

`request` 可以直接保留空白，因为我们不会请求或处理数据。我们还可以假设数据来源是 `None`，表明该代码不需要执行任何调用。响应仅返回“Hello World”。为了测试该解析器，我们需要使用该查询类型发出请求：

```
query helloWorldTest {
  helloWorld
}
```

以下是一个名为 `helloWorldTest` 的查询，它返回 `helloWorld` 字段。在执行时，`helloWorld` 字段解析器也会执行并返回响应：

```
{
  "data": {
    "helloWorld": "Hello World"
  }
}
```

像这样返回常量是您可以执行的最简单操作。实际上，您将返回输入、列表等。以下是一个更复杂的示例：

```
type Book {
  id: ID!
  title: String
}

type Query {
  getBooks: [Book]
}
```

此处，我们返回一个 `Books` 列表。假设我们使用 DynamoDB 表存储图书数据。我们的处理程序可能如下所示：

```
/**
 * Performs a scan on the dynamodb data source
 */
export function request(ctx) {
  return { operation: 'Scan' };
}

/**
 * return a list of scanned post items
 */
export function response(ctx) {
  return ctx.result.items;
}
```

我们的请求使用内置扫描操作搜索表中的所有条目，将结果存储在上下文中，然后将其传送到响应。响应获取结果项目，并在响应中返回它们：

```
{
  "data": {
    "getBooks": {
      "items": [
        {
          "id": "abcdefgh-1234-1234-1234-abcdefghijkl",
          "title": "book1"
        },
        {
          "id": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
          "title": "book2"
        },

        ...

      ]
    }
  }
}
```

## 解析器上下文
<a name="resolver-components-context"></a>

在解析器中，处理程序链中的每个步骤必须了解以前步骤中的数据状态。可以存储一个处理程序的结果，并将其作为参数传递给另一个处理程序。GraphQL 定义了 4 个基本解析器参数：


****  

| 解析器基本参数 | 说明 | 
| --- | --- | 
| obj root、parent、等 | 父项的结果。 | 
| args | 为 GraphQL 查询中的字段提供的参数。 | 
| context | 为每个解析器提供的值，其中保存重要上下文信息，例如当前登录的用户或数据库的访问权限。 | 
| info | 该值保存与当前查询相关的字段特定信息以及架构详细信息。 | 

在中 AWS AppSync，`[context](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-context-reference-js.html)`(ctx) 参数可以保存上述所有数据。它是根据请求创建的对象，并包含授权凭证、结果数据、错误、请求元数据等数据。上下文为程序员提供了一种简单方法，以处理来自请求的其他部分的数据。再看一下该代码片段：

```
/**
 * Performs a scan on the dynamodb data source
 */
export function request(ctx) {
  return { operation: 'Scan' };
}

/**
 * return a list of scanned post items
 */
export function response(ctx) {
  return ctx.result.items;
}
```

为请求提供了上下文 (ctx) 作为参数；这是请求的状态。它对表中的所有项目执行扫描，并将结果重新存储到 `result` 的上下文中。然后，将上下文传递给响应参数，该参数访问 `result` 并返回其内容。

## 请求和解析
<a name="resolver-ast"></a>

在对 GraphQL 服务进行查询时，它必须在执行之前执行解析和验证过程。将解析您的请求并转换为抽象语法树，并对您的架构运行多种验证算法以验证树内容。在执行验证步骤后，将遍历并处理树的节点，调用解析器，将结果存储在上下文中，然后返回响应。例如，使用以下查询：

```
query {
  Person {  //object type
    name  //scalar
    age   //scalar
  } 
}
```

我们返回具有 `name` 和 `age` 字段的 `Person`。在运行该查询时，树如下所示：

![\[Hierarchical diagram showing query, Person, name, and age nodes connected by arrows.\]](http://docs.aws.amazon.com/zh_cn/appsync/latest/devguide/images/ast-1.png)


从树中可以看出，该请求在架构中搜索根以查找 `Query`。在查询中，将解析 `Person` 字段。从前面的示例中，我们知道这可能是来自用户的输入、值列表等。`Person` 很可能与包含我们所需的字段（`name` 和 `age`）的对象类型相关联。在找到这两个子字段后，它们按给定的顺序进行解析（先解析 `name`，然后解析 `age`）。在完全解析树后，将完成请求并将其发回到客户端。

# GraphQL 的其他属性
<a name="graphql-properties"></a>

GraphQL 由多种设计原则组成，以在大规模系统中保持简单性和稳健性。

## 声明性
<a name="declarative-property"></a>

GraphQL 是声明性的，这意味着用户仅声明他们希望查询的字段以描述数据（设置形状）。响应仅返回这些属性的数据。例如，以下操作在 DynamoDB 表中检索 ISBN 13 值为的`Book`对象：`id`*9780199536061*

```
{
  getBook(id: "9780199536061") {
    name
    year
    author
  }
}
```

响应返回负载中的字段（`name`、`year` 和 `author`），而不返回任何其他内容：

```
{
  "data": {
    "getBook": {
      "name": "Anna Karenina",
      "year": "1878",
      "author": "Leo Tolstoy",
    }
  }
}
```

由于这种设计原则，GraphQL消除了REST在复杂系统中长期存在的过度吸引力和负担不足的问题。 APIs 这可以提高数据收集效率并提高网络性能。

## 分层
<a name="hierarchical-property"></a>

GraphQL 非常灵活，用户可以设置请求的数据形状以满足应用程序需求。请求的数据始终遵循 GraphQL API 中定义的属性的类型和语法。例如，以下代码段显示了使用名为的新字段范围的`getBook`操作`quotes`，该作用域返回所有存储的引号字符串和链接到的`Book`*9780199536061*页面：

```
{
  getBook(id: "9780199536061") {
    name
    year
    author
    quotes {
      description
      page
    }
  }
}
```

运行该查询将返回以下结果：

```
{
  "data": {
    "getBook": {
      "name": "Anna Karenina",
      "year": "1878",
      "author": "Leo Tolstoy",
      "quotes": [
         {
            "description": "The highest Petersburg society is essentially one: in it everyone knows everyone else, everyone even visits everyone else.",
            "page": 135
         },
         { 
            "description": "Happy families are all alike; every unhappy family is unhappy in its own way.",
            "page": 1
         },
         {        
            "description": "To Konstantin, the peasant was simply the chief partner in their common labor.",
            "page": 251
         }
      ]
    }
  }
}
```

正如您看到的一样，与请求的书籍关联的 `quotes` 字段以数组形式返回，其格式与查询描述的格式相同。尽管此处未显示，但 GraphQL 具有额外的优势，即不特别关注它检索的数据的位置。`Books` 和 `quotes` 可以是单独存储的，但只要存在关联，GraphQL 仍会检索该信息。这意味着，您的查询可以在单个请求中检索大量单独的数据。

## 内省
<a name="introspective-property"></a>

GraphQL 是自说明性的，或者说是内省的。它支持多种内置操作，以使用户能够查看架构中的基本类型和字段。例如，以下是具有 `date` 和 `description` 字段的 `Foo` 类型：

```
type Foo {
	date: String
	description: String
}
```

我们可以使用 `_type` 操作在架构下面查找类型元数据：

```
{
  __type(name: "Foo") {
    name                   # returns the name of the type
    fields {               # returns all fields in the type
      name                 # returns the name of each field
      type {               # returns all types for each field
        name               # returns the scalar type
      }
    }
  }
}
```

这会返回一个响应：

```
{
  "__type": {
    "name": "Foo",                     # The type name
    "fields": [
      {
        "name": "date",                # The date field
        "type": { "name": "String" }   # The date's type
      },
      {
        "name": "description",         # The description field
        "type": { "name": "String" }   # The description's type
      },
    ]
  }
}
```

该功能可用于找出特定 GraphQL 架构支持哪些类型和字段。GraphQL 支持各种这样的内省操作。有关更多信息，请参阅[内省](https://graphql.org/learn/introspection/)。

## 强类型
<a name="strong-typing-property"></a>

GraphQL 通过其类型和字段系统支持强类型。在架构中定义某些内容时，它必须具有可以在运行时之前验证的类型。它还必须遵循 GraphQL 的语法规范。这个概念与其他语言的编程没有什么不同。例如，以下是以前的 `Foo` 类型：

```
type Foo {
	date: String
	description: String
}
```

我们可以看到 `Foo` 是将创建的对象。在 `Foo` 实例中，具有一个 `date` 和 `description` 字段，它们都是 `String` 基元类型（标量）。从语法上讲，我们看到已声明 `Foo`，并且它的字段位于其范围内。这种类型检查和逻辑语法的组合确保您的 GraphQL API 简洁且不言自明。可以在[此处](https://spec.graphql.org/)找到 GraphQL 的类型和语法规范。