

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

# 使用以下方法设计 GraphQL APIs AWS AppSync
<a name="designing-a-graphql-api"></a>

AWS AppSync 允许您 APIs 使用控制台体验创建 GraphQL。您可以在[启动示例架构](https://docs.aws.amazon.com/appsync/latest/devguide/quickstart.html)一节中大致了解该内容。但是，该指南并未显示您可以利用的选项和配置的完整目录 AWS AppSync。

当您选择在控制台中创建 GraphQL API 时，有多个选项可供您使用。如果您按照我们的[启动示例架构](https://docs.aws.amazon.com/appsync/latest/devguide/quickstart.html)指南进行操作，我们已向您说明了如何通过预定义的模型创建 API。在以下各节中，我们将引导您了解在中创建 GraphQL APIs 的其余选项和配置。 AWS AppSync

在本节中，您将回顾以下概念：

1. [Blank APIs or imports](blank-import-api.md#aws-appsync-blank-import-api)：本指南详细介绍了创建 GraphQL API 的完整过程。您将了解如何通过没有模型的空白模板创建 GraphQL，为架构配置数据来源以及将第一个解析器添加到字段中。

1. [Real-time data](aws-appsync-real-time-data.md#aws-appsync-real-time-data-anchor)：本指南将向您展示使用 WebSocket 引擎创建 API AWS AppSync的潜在选项。

1. [Merged APIs](merged-api.md#aws-appsync-merged-api)：本指南将向您展示如何 APIs 通过关联和合并来自多个现有 GraphQL 的数据来创建新的 GraphQL。 APIs

1. [使用 RDS 内省构建 GraphQ APIs L](rds-introspection.md)：本指南将向您展示如何使用数据 API 集成 Amazon RDS 表。

# 构建 GraphQL API（空白或已导入） APIs
<a name="blank-import-api"></a>

在通过空白模板创建 GraphQL API 之前，回顾一下有关 GraphQL 的概念会有所帮助。GraphQL API 具有三个基本组件：

1. **架构**是包含数据形状和定义的文件。在客户端向您的 GraphQL 服务发出请求时，返回的数据将遵循架构规范。有关更多信息，请参阅 [GraphQL 架构](schema-components.md#aws-appsync-schema-components)。

1. **数据来源**附加到您的架构。在发出请求时，这是检索和修改数据的地方。有关更多信息，请参阅 [数据来源](data-source-components.md#aws-appsync-data-source-components)。

1. **解析器**位于架构和数据来源之间。在发出请求时，解析器对来自数据来源的数据执行操作，然后返回结果以作为响应。有关更多信息，请参阅 [解析器](resolver-components.md#aws-appsync-resolver-components)。

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


AWS AppSync APIs 通过允许您创建、编辑和存储架构和解析器的代码来管理您的。您的数据来源来自外部存储库，例如数据库、DynamoDB 表和 Lambda 函数。如果您正在使用 AWS 服务来存储数据或计划这样做，则在将 AWS 账户中的数据与 GraphQL 关联时，可以 AWS AppSync 提供近乎无缝的体验。 APIs

在下一节中，您将学习如何使用该 AWS AppSync 服务创建每个组件。

**Topics**
+ [设计您的 GraphQL 架构](designing-your-schema.md)
+ [附加数据来源](attaching-a-data-source.md)
+ [配置 AWS AppSync 解析器](resolver-config-overview.md)
+ [APIs 与 CDK 一起使用](using-your-api.md)

# 设计您的 GraphQL 架构
<a name="designing-your-schema"></a>

GraphQL 架构是任何 GraphQL 服务器实施的基础。每个 GraphQL API 由**单个**架构定义，其中包含描述如何填充请求中的数据的类型和字段。必须根据架构验证流经 API 的数据和执行的操作。

一般来说，[GraphQL 类型系统](https://graphql.org/learn/schema/#type-system)描述 GraphQL 服务器的功能，并用于确定查询是否有效。服务器的类型系统通常称为该服务器的架构，可以由不同的对象类型、标量类型、输入类型等组成。GraphQL 既是声明性的，又是强类型的，这意味着将在运行时明确定义类型，并且仅返回指定的内容。

AWS AppSync 允许您定义和配置 GraphQL 架构。以下部分介绍如何使用 AWS AppSync的服务从头开始创建 GraphQL 架构。

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

**提示**  
我们建议在继续之前查看[架构](https://docs.aws.amazon.com//appsync/latest/devguide/schema-components.html)一节。

GraphQL 是一种实施 API 服务的强大工具。根据 [GraphQL 网站](https://graphql.org/)，GraphQL 定义如下：

“*GraphQL 是一种查询语言，也是使用现有数据完成这些查询的运行时。 APIs GraphQL 为你的 API 中的数据提供了完整且易于理解的描述，使客户能够准确地询问他们需要什么，仅此而已，使其更容易随着 APIs 时间的推移而发展，并支持强大的开发者工具。* “

本节介绍了 GraphQL 实施的第一部分，即架构。根据上面的引述，架构扮演“为 API 中的数据提供完整且易于理解的描述”角色。换句话说，GraphQL 架构是您的服务的数据、操作以及它们之间的关系的文本表示形式。架构被视为 GraphQL 服务实施的主要入口点。毫不奇怪，它通常是您在项目中首先实施的内容之一。我们建议在继续之前查看[架构](https://docs.aws.amazon.com//appsync/latest/devguide/schema-components.html)一节。

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

最简单的架构包含三种不同的数据类别：

1. **架构根**：根定义架构的入口点。它指向对数据执行某种操作（例如添加、删除或修改某些内容）的字段。

1. **类型**：这些是用于表示数据形状的基本类型。您几乎可以将它们视为具有定义的特征的事物的对象或抽象表示形式。例如，您可以创建 `Person` 对象以表示数据库中的某个人。每个人的特征将在 `Person` 中定义为字段。它们可能是这个人的姓名、年龄、工作、地址等任何内容。

1. **特殊对象类型**：这些是在架构中定义操作行为的类型。每种特殊对象类型在每个架构中定义一次。它们先放置在架构根中，然后在架构正文中进行定义。特殊对象类型中的每个字段定义解析器实施的单个操作。

为了便于理解，假设您正在创建一个服务以存储作者及其所写的书籍。每个作者具有姓名和他们撰写的一系列书籍。每本书具有名称和相关的作者列表。我们还希望能够添加或检索书籍和作者。这种关系的简单 UML 表示形式可能如下所示：

![\[UML diagram showing Author and Book classes with attributes and methods, linked by association.\]](http://docs.aws.amazon.com/zh_cn/appsync/latest/devguide/images/GraphQL-UML-1.png)


在 GraphQL 中，`Author` 和 `Book` 实体表示架构中的两种不同的对象类型：

```
type Author {
}

type Book {
}
```

`Author` 包含 `authorName` 和 `Books`，而 `Book` 包含 `bookName` 和 `Authors`。这些可以表示为类型范围内的字段：

```
type Author {
  authorName: String
  Books: [Book]
}

type Book {
  bookName: String
  Authors: [Author]
}
```

正如您看到的一样，类型表示形式与图表非常接近。不过，这些方法可能会变得有些复杂。它们将作为字段放置在几种特殊对象类型之一中。它们的特殊对象分类取决于它们的行为。GraphQL 包含三种基本的特殊对象类型：查询、变更和订阅。有关更多信息，请参阅[特殊对象](https://docs.aws.amazon.com//appsync/latest/devguide/graphql-types.html#special-object-components)。

由于 `getAuthor` 和 `getBook` 都请求数据，因此，它们将放置在 `Query` 特殊对象类型中：

```
type Author {
  authorName: String
  Books: [Book]
}

type Book {
  bookName: String
  Authors: [Author]
}

type Query {
  getAuthor(authorName: String): Author
  getBook(bookName: String): Book
}
```

这些操作链接到查询，而查询本身链接到架构。添加架构根会将特殊对象类型（该示例中的 `Query`）定义为入口点之一。可以使用 `schema` 关键字完成该操作：

```
schema {
  query: Query
}

type Author {
  authorName: String
  Books: [Book]
}

type Book {
  bookName: String
  Authors: [Author]
}

type Query {
  getAuthor(authorName: String): Author
  getBook(bookName: String): Book
}
```

看一下最后两个方法（`addAuthor` 和 `addBook`），它们在数据库中添加数据，因此，它们是在 `Mutation` 特殊对象类型中定义的。不过，从[类型](https://docs.aws.amazon.com/appsync/latest/devguide/graphql-types.html#input-components)页面中，我们还知道不允许直接引用对象的输入，因为它们严格来说是输出类型。在这种情况下，我们不能使用 `Author` 或 `Book`，因此，我们需要创建一种具有相同字段的输入类型。在该示例中，我们添加了 `AuthorInput` 和 `BookInput`，它们接受相应类型的相同字段。然后，我们将输入作为参数以创建变更：

```
schema {
  query: Query
  mutation: Mutation
}

type Author {
  authorName: String
  Books: [Book]
}

input AuthorInput {
  authorName: String
  Books: [BookInput]
}

type Book {
  bookName: String
  Authors: [Author]
}

input BookInput {
  bookName: String
  Authors: [AuthorInput]
}

type Query {
  getAuthor(authorName: String): Author
  getBook(bookName: String): Book
}

type Mutation {
  addAuthor(input: [BookInput]): Author
  addBook(input: [AuthorInput]): Book
}
```

让我们回顾一下我们刚刚执行的操作：

1. 我们创建了一个具有 `Book` 和 `Author` 类型的架构以表示我们的实体。

1. 我们添加了包含实体特性的字段。

1. 我们添加了一个查询，以从数据库中检索该信息。

1. 我们添加了一个变更以处理数据库中的数据。

1. 我们添加了输入类型以在变更中替换对象参数，从而符合 GraphQL 的规则。

1. 我们将查询和变更添加到根架构中，以使 GraphQL 实施了解根类型位置。

正如您看到的一样，创建架构的过程通常采用数据建模（尤其是数据库建模）中的一些概念。您可以将架构视为适合源数据的形状。它还充当解析器实施的模型。在以下各节中，您将学习如何使用各种 AWS支持的工具和服务来创建架构。

**注意**  
以下几节中的示例并不表示在实际应用程序中运行。它们仅用于说明这些命令，以使您可以构建自己的应用程序。

## 创建架构
<a name="creating-schema"></a>

您的架构将位于名为的文件中`schema.graphql`。 AWS AppSync 允许用户使用各种方法为其 G APIs raphQL 创建新的架构。在该示例中，我们将创建一个空白 API 以及空白架构。

------
#### [ Console ]

1. 登录 AWS 管理控制台 并打开[AppSync控制台](https://console.aws.amazon.com/appsync/)。

   1. 在**控制面板**中，选择**创建 API**。

   1. **在 **API 选项下，选择** **GraphQL APIs**、**从头开始设计**，然后选择 “下一步”。**

      1. 对于 **API 名称**，将预填充的名称更改为您的应用程序所需的名称。

      1. 对于**联系信息**，您可以输入联系人以指定 API 的管理员。此为可选字段。

      1. 在**私有 API 配置**下面，您可以启用私有 API 功能。只能从配置的 VPC 终端节点 (VPCE) 中访问私有 API。有关更多信息，请参阅[私有 APIs](https://docs.aws.amazon.com/appsync/latest/devguide/using-private-apis.html)。

         对于该示例，我们不建议启用该功能。在检查您的输入后，选择**下一步**。

   1. 在**创建 GraphQL 类型**下面，您可以选择创建 DynamoDB 表以用作数据来源，或者跳过该步骤并稍后执行。

      对于该示例，请选择**稍后创建 GraphQL 资源**。我们将在单独的章节中创建资源。

   1. 检查您的输入，然后选择**创建 API**。

1. 将进入您的特定 API 的控制面板。由于该 API 的名称位于控制面板顶部，因此，您可以看出这一点。如果不是这样，你可以在**侧栏**APIs****中选择，然后在**APIs 控制面板**中选择你的 API。

   1. 在**侧边栏**中，在您的 API 名称下面选择**架构**。

1. 在**架构编辑器**中，您可以配置您的 `schema.graphql` 文件。它可能是空的，也可能填充了通过模型生成的类型。右侧是**解析器**部分，用于将解析器附加到您的架构字段。我们不会在本节中介绍解析器内容。

------
#### [ CLI ]

**注意**  
在使用 CLI 时，请确保您具有正确权限以在该服务中访问和创建资源。您可能希望为需要访问该服务的非管理员用户设置[最低权限](https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html#grant-least-privilege)策略。有关 AWS AppSync 策略的更多信息，请参阅的[身份和访问管理 AWS AppSync](https://docs.aws.amazon.com//appsync/latest/devguide/security-iam.html)。  
此外，如果您还没有查看控制台版本，我们建议您先查看该版本。

1. 如果您尚未[安装](https://docs.aws.amazon.com//cli/latest/userguide/cli-chap-getting-started.html) AWS CLI 并添加您的[配置](https://docs.aws.amazon.com//cli/latest/userguide/cli-configure-quickstart.html)，请执行该操作。

1. 运行 [https://docs.aws.amazon.com/cli/latest/reference/appsync/create-graphql-api.html](https://docs.aws.amazon.com/cli/latest/reference/appsync/create-graphql-api.html) 命令以创建 GraphQL API 对象。

   您需要为该特定命令键入两个参数：

   1. 您的 API 的 `name`。

   1. `authentication-type` 或用于访问该 API 的凭证类型（IAM、OIDC 等）。
**注意**  
必须配置其他参数（例如 `Region`），但这些参数通常默认为您的 CLI 配置值。

   示例命令可能如下所示：

   ```
   aws appsync create-graphql-api --name testAPI123 --authentication-type API_KEY
   ```

   将在 CLI 中返回输出。示例如下：

   ```
   {
       "graphqlApi": {
           "xrayEnabled": false,
           "name": "testAPI123",
           "authenticationType": "API_KEY",
           "tags": {},
           "apiId": "abcdefghijklmnopqrstuvwxyz",
           "uris": {
               "GRAPHQL": "https://zyxwvutsrqponmlkjihgfedcba.appsync-api.us-west-2.amazonaws.com/graphql",
               "REALTIME": "wss://zyxwvutsrqponmlkjihgfedcba.appsync-realtime-api.us-west-2.amazonaws.com/graphql"
           },
           "arn": "arn:aws:appsync:us-west-2:107289374856:apis/abcdefghijklmnopqrstuvwxyz"
       }
   }
   ```

1. 
**注意**  
这是一个可选命令，它采用现有的架构并使用 Base64 Blob 将其上传到 AWS AppSync 服务中。对于该示例，我们不会使用该命令。

   运行 [https://docs.aws.amazon.com/cli/latest/reference/appsync/start-schema-creation.html](https://docs.aws.amazon.com/cli/latest/reference/appsync/start-schema-creation.html) 命令。

   您需要为该特定命令键入两个参数：

   1. 上一步中的 `api-id`。

   1. 架构 `definition` 是一个 Base64 编码的二进制 Blob。

   示例命令可能如下所示：

   ```
    aws appsync start-schema-creation --api-id abcdefghijklmnopqrstuvwxyz --definition "aa1111aa-123b-2bb2-c321-12hgg76cc33v"
   ```

   将返回输出：

   ```
   {
       "status": "PROCESSING"
   }
   ```

   该命令不会在处理后返回最终输出。您必须使用单独的命令 ([https://awscli.amazonaws.com/v2/documentation/api/latest/reference/appsync/get-schema-creation-status.html](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/appsync/get-schema-creation-status.html)) 查看结果。请注意，这两个命令是异步的，因此，即使仍在创建架构，您也可以检查输出状态。

------
#### [ CDK ]

**提示**  
在使用 CDK 之前，我们建议您查看 CDK 的[官方文档](https://docs.aws.amazon.com/cdk/v2/guide/home.html)以及 [CD](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_appsync-readme.html) K 参考资料。 AWS AppSync  
下面列出的步骤仅显示用于添加特定资源的一般代码片段示例。这**并不**意味着，它是您的生产代码中的有效解决方案。我们还假设您已具有正常工作的应用程序。

1. CDK 的起点有所不同。理想情况下，应该已创建了您的 `schema.graphql` 文件。您只需创建一个具有 `.graphql` 文件扩展名的新文件。它可以是空文件。

1. 一般来说，您可能需要将 import 指令添加到您使用的服务中。例如，它可能采用以下形式：

   ```
   import * as x from 'x'; # import wildcard as the 'x' keyword from 'x-service'
   import {a, b, ...} from 'c'; # import {specific constructs} from 'c-service'
   ```

   要添加 GraphQL API，您的堆栈文件需要导入 AWS AppSync 服务：

   ```
   import * as appsync from 'aws-cdk-lib/aws-appsync';
   ```
**注意**  
这意味着我们使用 `appsync` 关键字导入整个服务。要在您的应用程序中使用它，您的 AWS AppSync 构造将使用以下格式`appsync.construct_name`。例如，如果我们要创建 GraphQL API，我们将使用 `new appsync.GraphqlApi(args_go_here)`。以下步骤介绍了这一点。

1. 最基本的 GraphQL API 将包括 API 的 `name` 和 `schema` 路径。

   ```
   const add_api = new appsync.GraphqlApi(this, 'API_ID', {
     name: 'name_of_API_in_console',
     schema: appsync.SchemaFile.fromAsset(path.join(__dirname, 'schema_name.graphql')),
   });
   ```
**注意**  
让我们回顾一下该代码片段执行的操作。在 `api` 范围内，我们调用 `appsync.GraphqlApi(scope: Construct, id: string, props: GraphqlApiProps)` 以创建一个新的 GraphQL API。范围是 `this`，它指的是当前对象。ID 是*API\$1ID*，这将是你的 GraphQL API 在创建 CloudFormation 时使用的资源名称。`GraphqlApiProps` 包含 GraphQL API 的 `name` 和 `schema`。`schema`将通过在绝对路径 (`SchemaFile.fromAsset`) 中搜索`.graphql`文件 (`__dirname`) 来生成架构 (*schema\$1name.graphql*)。在实际场景中，您的架构文件可能位于 CDK 应用程序内。  
要使用对 GraphQL API 所做的更改，您必须重新部署该应用程序。

------

## 在架构中添加类型
<a name="adding-schema-types"></a>

现已添加了架构，您可以开始添加输入和输出类型。请注意，不应在实际代码中使用此处的类型；它们只是帮助您理解该过程的示例。

首先，我们创建一个对象类型。在实际代码中，您不必从这些类型开始。只要遵循 GraphQL 的规则和语法，您可以随时创建所需的任何类型。

**注意**  
接下来的几节将使用**架构编辑器**，因此，请将其保持打开状态。

------
#### [ Console ]
+ 您可以使用 `type` 关键字和类型名称以创建对象类型：

  ```
  type Type_Name_Goes_Here {}
  ```

  在类型的范围内，您可以添加表示对象特性的字段：

  ```
  type Type_Name_Goes_Here {
    # Add fields here
  }
  ```

  示例如下：

  ```
  type Obj_Type_1 {
    id: ID!
    title: String
    date: AWSDateTime
  }
  ```
**注意**  
在该骤中，我们添加了一个通用对象类型，将 `id` 必填字段存储为 `ID`，将 `title` 字段存储为 `String`，并将 `date` 字段存储为 `AWSDateTime`。要查看类型和字段列表及其用途，请参阅[架构](https://docs.aws.amazon.com//appsync/latest/devguide/schema-components.html)。要查看标量列表及其用途，请参阅[类型参考](https://docs.aws.amazon.com/appsync/latest/devguide/type-reference.html)。

------
#### [ CLI ]

**注意**  
如果您还没有查看控制台版本，我们建议您先查看该版本。
+ 您可以运行 [https://awscli.amazonaws.com/v2/documentation/api/latest/reference/appsync/create-type.html](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/appsync/create-type.html) 命令以创建对象类型。

  您需要为该特定命令输入一些参数：

  1. 您的 API 的 `api-id`。

  1. `definition` 或您的类型内容。在控制台示例中，这是：

     ```
     type Obj_Type_1 {
       id: ID!
       title: String
       date: AWSDateTime
     }
     ```

  1. 您的输入的 `format`。在该示例中，我们使用 `SDL`。

  示例命令可能如下所示：

  ```
  aws appsync create-type --api-id abcdefghijklmnopqrstuvwxyz --definition "type Obj_Type_1{id: ID! title: String date: AWSDateTime}" --format SDL
  ```

  将在 CLI 中返回输出。示例如下：

  ```
  {
      "type": {
          "definition": "type Obj_Type_1{id: ID! title: String date: AWSDateTime}",
          "name": "Obj_Type_1",
          "arn": "arn:aws:appsync:us-west-2:107289374856:apis/abcdefghijklmnopqrstuvwxyz/types/Obj_Type_1",
          "format": "SDL"
      }
  }
  ```
**注意**  
在该骤中，我们添加了一个通用对象类型，将 `id` 必填字段存储为 `ID`，将 `title` 字段存储为 `String`，并将 `date` 字段存储为 `AWSDateTime`。要查看类型和字段列表及其用途，请参阅[架构](https://docs.aws.amazon.com//appsync/latest/devguide/schema-components.html)。要查看标量列表及其用途，请参阅[类型参考](https://docs.aws.amazon.com/appsync/latest/devguide/type-reference.html)。  
此外，您可能已意识到，直接输入定义适用于较小的类型，但对于添加较大类型或多个类型是不可行的。您可以选择将所有内容添加到 `.graphql` 文件中，然后[将其作为输入传递](https://docs.aws.amazon.com/cli/latest/userguide/cli-usage-parameters-file.html)。

------
#### [ CDK ]

**提示**  
在使用 CDK 之前，我们建议您查看 CDK 的[官方文档](https://docs.aws.amazon.com/cdk/v2/guide/home.html)以及 [CD](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_appsync-readme.html) K 参考资料。 AWS AppSync  
下面列出的步骤仅显示用于添加特定资源的一般代码片段示例。这**并不**意味着，它是您的生产代码中的有效解决方案。我们还假设您已具有正常工作的应用程序。

要添加类型，您需要将其添加到您的 `.graphql` 文件中。例如，控制台示例是：

```
type Obj_Type_1 {
  id: ID!
  title: String
  date: AWSDateTime
}
```

您可以像任何其他文件一样将类型直接添加到架构中。

**注意**  
要使用对 GraphQL API 所做的更改，您必须重新部署该应用程序。

------

[对象类型](https://graphql.org/learn/schema/#object-types-and-fields)具有[标量类型的](https://graphql.org/learn/schema/#scalar-types)字段，例如字符串和整数。 AWS AppSync 除基本的 GraphQL 标量外，还允许您使用增强的标量类型。`AWSDateTime`此外，任何以感叹号结尾的字段都是必填字段。

特别是，`ID` 标量类型是唯一标识符，可以是 `String` 或 `Int`。您可以在解析器代码中控制这些内容以自动进行分配。

特殊对象类型（如 `Query`）和“常规”对象类型（如上面的示例）之间存在相似之处，因为它们都使用 `type` 关键字并被视为对象。不过，对于特殊对象类型（`Query`、`Mutation` 和 `Subscription`），它们的行为有很大不同，因为它们是作为 API 的入口点公开的。它们更多地涉及设置形状操作而不是数据。有关更多信息，请参阅[查询和变更类型](https://graphql.org/learn/schema/#the-query-and-mutation-types)。

对于特殊对象类型主题，下一步可能是添加一个或多个对象类型以对设置形状的数据执行操作。在实际场景中，每个 GraphQL 架构必须至少具有一个根查询类型以请求数据。您可以将查询视为 GraphQL 服务器的入口点（或终端节点）之一。让我们添加一个查询以作为示例。

------
#### [ Console ]
+ 要创建查询，您只需将其添加到架构文件中，就像任何其他类型一样。查询需要具有 `Query` 类型并在根中具有一个条目，如下所示：

  ```
  schema {
    query: Name_of_Query
  }
  
  type Name_of_Query {
    # Add field operation here
  }
  ```

  请注意，*Name\$1of\$1Query*在生产环境中，大多数`Query`情况下只会被调用。我们建议保留该值。在查询类型中，您可以添加字段。每个字段都会在请求中执行一个操作。因此，大多数（即使不是全部）字段将附加到一个解析器。不过，我们在本节中并不关注这个问题。对于字段操作格式，它可能如下所示：

  ```
  Name_of_Query(params): Return_Type # version with params
  Name_of_Query: Return_Type # version without params
  ```

  示例如下：

  ```
  schema {
    query: Query
  }
  
  type Query {
    getObj: [Obj_Type_1]
  }
  
  type Obj_Type_1 {
    id: ID!
    title: String
    date: AWSDateTime
  }
  ```
**注意**  
在该步骤中，我们添加了一个 `Query` 类型，并在 `schema` 根中定义该类型。我们的 `Query` 类型定义了一个 `getObj` 字段，该字段返回 `Obj_Type_1` 对象列表。请注意，`Obj_Type_1` 是上一步中的对象。在生产代码中，您的字段操作通常处理由 `Obj_Type_1` 等对象设置形状的数据。此外，`getObj` 等字段通常具有一个解析器以执行业务逻辑。将在另一节中介绍该内容。  
另外，在导出过程中 AWS AppSync 会自动添加架构根，因此从技术上讲，您不必将其直接添加到架构中。我们的服务自动处理重复的架构。我们在此处添加架构根以作为最佳实践。

------
#### [ CLI ]

**注意**  
如果您还没有查看控制台版本，我们建议您先查看该版本。

1. 运行 [https://awscli.amazonaws.com/v2/documentation/api/latest/reference/appsync/create-type.html](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/appsync/create-type.html) 命令以创建一个具有 `query` 定义的 `schema` 根。

   您需要为该特定命令输入一些参数：

   1. 您的 API 的 `api-id`。

   1. `definition` 或您的类型内容。在控制台示例中，这是：

      ```
      schema {
        query: Query
      }
      ```

   1. 您的输入的 `format`。在该示例中，我们使用 `SDL`。

   示例命令可能如下所示：

   ```
   aws appsync create-type --api-id abcdefghijklmnopqrstuvwxyz --definition "schema {query: Query}" --format SDL
   ```

   将在 CLI 中返回输出。示例如下：

   ```
   {
       "type": {
           "definition": "schema {query: Query}",
           "name": "schema",
           "arn": "arn:aws:appsync:us-west-2:107289374856:apis/abcdefghijklmnopqrstuvwxyz/types/schema",
           "format": "SDL"
       }
   }
   ```
**注意**  
请注意，如果您在 `create-type` 命令中未正确输入某些内容，您可以运行 [https://awscli.amazonaws.com/v2/documentation/api/latest/reference/appsync/update-type.html](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/appsync/update-type.html) 命令以更新您的架构根（或架构中的任何类型）。在该示例中，我们暂时更改架构根以包含 `subscription` 定义。  
您需要为该特定命令输入一些参数：  
您的 API 的 `api-id`。
您的类型的 `type-name`。在控制台示例中，这是 `schema`。
`definition` 或您的类型内容。在控制台示例中，这是：  

      ```
      schema {
        query: Query
      }
      ```
添加 `subscription` 后的架构如下所示：  

      ```
      schema {
        query: Query
        subscription: Subscription
      }
      ```
您的输入的 `format`。在该示例中，我们使用 `SDL`。
示例命令可能如下所示：  

   ```
   aws appsync update-type --api-id abcdefghijklmnopqrstuvwxyz --type-name schema --definition "schema {query: Query subscription: Subscription}" --format SDL
   ```
将在 CLI 中返回输出。示例如下：  

   ```
   {
       "type": {
           "definition": "schema {query: Query subscription: Subscription}",
           "arn": "arn:aws:appsync:us-west-2:107289374856:apis/abcdefghijklmnopqrstuvwxyz/types/schema",
           "format": "SDL"
       }
   }
   ```
在该示例中，添加预设置格式的文件仍然有效。

1. 运行 [https://awscli.amazonaws.com/v2/documentation/api/latest/reference/appsync/create-type.html](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/appsync/create-type.html) 命令以创建一个 `Query` 类型。

   您需要为该特定命令输入一些参数：

   1. 您的 API 的 `api-id`。

   1. `definition` 或您的类型内容。在控制台示例中，这是：

      ```
      type Query {
        getObj: [Obj_Type_1]
      }
      ```

   1. 您的输入的 `format`。在该示例中，我们使用 `SDL`。

   示例命令可能如下所示：

   ```
   aws appsync create-type --api-id abcdefghijklmnopqrstuvwxyz --definition "type Query {getObj: [Obj_Type_1]}" --format SDL
   ```

   将在 CLI 中返回输出。示例如下：

   ```
   {
       "type": {
           "definition": "Query {getObj: [Obj_Type_1]}",
           "name": "Query",
           "arn": "arn:aws:appsync:us-west-2:107289374856:apis/abcdefghijklmnopqrstuvwxyz/types/Query",
           "format": "SDL"
       }
   }
   ```
**注意**  
在该步骤中，我们添加了一个 `Query` 类型，并在 `schema` 根中定义该类型。我们的 `Query` 类型定义了一个 `getObj` 字段，该字段返回 `Obj_Type_1` 对象列表。  
在 `schema` 根代码 `query: Query` 中，`query:` 部分指示在您的架构中定义了一个查询，而 `Query` 部分指示实际的特殊对象名称。

------
#### [ CDK ]

**提示**  
在使用 CDK 之前，我们建议您查看 CDK 的[官方文档](https://docs.aws.amazon.com/cdk/v2/guide/home.html)以及 [CD](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_appsync-readme.html) K 参考资料。 AWS AppSync  
下面列出的步骤仅显示用于添加特定资源的一般代码片段示例。这**并不**意味着，它是您的生产代码中的有效解决方案。我们还假设您已具有正常工作的应用程序。

您需要将查询和架构根添加到 `.graphql` 文件中。我们的示例与以下示例类似，但您希望将其替换为实际的架构代码：

```
schema {
  query: Query
}

type Query {
  getObj: [Obj_Type_1]
}

type Obj_Type_1 {
  id: ID!
  title: String
  date: AWSDateTime
}
```

您可以像任何其他文件一样将类型直接添加到架构中。

**注意**  
更新架构根是可选的。我们在该示例中添加架构根以作为最佳实践。  
要使用对 GraphQL API 所做的更改，您必须重新部署该应用程序。

------

您现已看到创建对象和特殊对象（查询）的示例。您还了解了这些对象如何相互关联以描述数据和操作。您可以具有仅包含数据描述以及一个或多个查询的架构。不过，我们希望添加另一个操作以将数据添加到数据来源中。我们将添加另一个名为 `Mutation` 的特殊对象类型以修改数据。

------
#### [ Console ]
+ 一个变更命名为 `Mutation`。与 `Query` 一样，`Mutation` 中的字段操作描述一个操作并附加到一个解析器。另请注意，我们需要在 `schema` 根中定义该变更，因为它是一个特殊对象类型。下面是一个变更示例：

  ```
  schema {
    mutation: Name_of_Mutation
  }
  
  type Name_of_Mutation {
    # Add field operation here
  }
  ```

  像查询一样，将在根中列出典型的变更。变异是使用`type`关键字和名称定义的。 *Name\$1of\$1Mutation*通常会被调用`Mutation`，所以我们建议保持这种状态。每个字段还会执行一个操作。对于字段操作格式，它可能如下所示：

  ```
  Name_of_Mutation(params): Return_Type # version with params
  Name_of_Mutation: Return_Type # version without params
  ```

  示例如下：

  ```
  schema {
    query: Query
    mutation: Mutation
  }
  
  type Obj_Type_1 {
    id: ID!
    title: String
    date: AWSDateTime
  }
  
  type Query {
    getObj: [Obj_Type_1]
  }
  
  type Mutation {
    addObj(id: ID!, title: String, date: AWSDateTime): Obj_Type_1
  }
  ```
**注意**  
在该步骤中，我们添加了一个具有 `addObj` 字段的 `Mutation` 类型。让我们简要说明一下该字段的用途：  

  ```
  addObj(id: ID!, title: String, date: AWSDateTime): Obj_Type_1
  ```
`addObj` 使用 `Obj_Type_1` 对象以执行操作。由于这些字段，这是显而易见的，但语法在 `: Obj_Type_1` 返回类型中证实了这一点。在 `addObj` 中，它接受 `Obj_Type_1` 对象中的 `id`、`title` 和 `date` 字段以作为参数。正如您看到的一样，它看起来很像方法声明。不过，我们还没有介绍我们的方法的行为。正如前面所述，架构仅用于定义数据和操作是什么，而不定义它们的工作方式。在我们稍后创建第一个解析器时，将实施实际的业务逻辑。  
在完成架构后，可以选择将其导出为 `schema.graphql` 文件。在**架构编辑器**中，您可以选择**导出架构**以使用支持的格式下载该文件。  
另外，在导出过程中 AWS AppSync 会自动添加架构根，因此从技术上讲，您不必将其直接添加到架构中。我们的服务自动处理重复的架构。我们在此处添加架构根以作为最佳实践。

------
#### [ CLI ]

**注意**  
如果您还没有查看控制台版本，我们建议您先查看该版本。

1. 运行 [https://awscli.amazonaws.com/v2/documentation/api/latest/reference/appsync/update-type.html](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/appsync/update-type.html) 命令以更新您的根架构。

   您需要为该特定命令输入一些参数：

   1. 您的 API 的 `api-id`。

   1. 您的类型的 `type-name`。在控制台示例中，这是 `schema`。

   1. `definition` 或您的类型内容。在控制台示例中，这是：

      ```
      schema {
        query: Query
        mutation: Mutation
      }
      ```

   1. 您的输入的 `format`。在该示例中，我们使用 `SDL`。

   示例命令可能如下所示：

   ```
   aws appsync update-type --api-id abcdefghijklmnopqrstuvwxyz --type-name schema --definition "schema {query: Query mutation: Mutation}" --format SDL
   ```

   将在 CLI 中返回输出。示例如下：

   ```
   {
       "type": {
           "definition": "schema {query: Query mutation: Mutation}",
           "arn": "arn:aws:appsync:us-west-2:107289374856:apis/abcdefghijklmnopqrstuvwxyz/types/schema",
           "format": "SDL"
       }
   }
   ```

1. 运行 [https://awscli.amazonaws.com/v2/documentation/api/latest/reference/appsync/create-type.html](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/appsync/create-type.html) 命令以创建一个 `Mutation` 类型。

   您需要为该特定命令输入一些参数：

   1. 您的 API 的 `api-id`。

   1. `definition` 或您的类型内容。在控制台示例中，这是：

      ```
      type Mutation {
        addObj(id: ID!, title: String, date: AWSDateTime): Obj_Type_1
      }
      ```

   1. 您的输入的 `format`。在该示例中，我们使用 `SDL`。

   示例命令可能如下所示：

   ```
   aws appsync create-type --api-id abcdefghijklmnopqrstuvwxyz --definition "type Mutation {addObj(id: ID! title: String date: AWSDateTime): Obj_Type_1}" --format SDL
   ```

   将在 CLI 中返回输出。示例如下：

   ```
   {
       "type": {
           "definition": "type Mutation {addObj(id: ID! title: String date: AWSDateTime): Obj_Type_1}",
           "name": "Mutation",
           "arn": "arn:aws:appsync:us-west-2:107289374856:apis/abcdefghijklmnopqrstuvwxyz/types/Mutation",
           "format": "SDL"
       }
   }
   ```

------
#### [ CDK ]

**提示**  
在使用 CDK 之前，我们建议您查看 CDK 的[官方文档](https://docs.aws.amazon.com/cdk/v2/guide/home.html)以及 [CD](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_appsync-readme.html) K 参考资料。 AWS AppSync  
下面列出的步骤仅显示用于添加特定资源的一般代码片段示例。这**并不**意味着，它是您的生产代码中的有效解决方案。我们还假设您已具有正常工作的应用程序。

您需要将查询和架构根添加到 `.graphql` 文件中。我们的示例与以下示例类似，但您希望将其替换为实际的架构代码：

```
schema {
  query: Query
  mutation: Mutation
}

type Obj_Type_1 {
  id: ID!
  title: String
  date: AWSDateTime
}

type Query {
  getObj: [Obj_Type_1]
}

type Mutation {
  addObj(id: ID!, title: String, date: AWSDateTime): Obj_Type_1
}
```

**注意**  
更新架构根是可选的。我们在该示例中添加架构根以作为最佳实践。  
要使用对 GraphQL API 所做的更改，您必须重新部署该应用程序。

------

## 可选注意事项 - 将枚举作为状态
<a name="optional-consideration-enums"></a>

至此，您知道如何创建基本架构了。不过，您可以添加很多内容以增加架构的功能。在应用程序中，一种常见情况是将枚举作为状态。您可以使用枚举在调用时强制从一组值中选择一个特定的值。对于您知道在很长一段时间内不会发生显著变化的内容，这是非常有用的。假设来说，我们可以添加一个枚举以在响应中返回状态代码或字符串。

例如，假设我们创建一个社交媒体应用程序，该应用程序在后端存储用户的文章数据。我们的架构包含一个 `Post` 类型，它表示一篇文章的数据：

```
type Post {
  id: ID!
  title: String
  date: AWSDateTime
  poststatus: PostStatus
}
```

我们的 `Post` 将包含唯一的 `id`、文章 `title`、发布 `date` 以及名为 `PostStatus` 的枚举，它表示应用程序处理时的文章状态。对于我们的操作，我们使用一个查询以返回所有文章数据：

```
type Query {
  getPosts: [Post]
}
```

我们还使用一个变更以将文章添加到数据来源中：

```
type Mutation {
  addPost(id: ID!, title: String, date: AWSDateTime, poststatus: PostStatus): Post
}
```

看一下我们的架构，`PostStatus` 枚举可能具有多种状态。我们可能需要三种基本状态，分别命名为 `success`（已成功处理文章）、`pending`（正在处理文章）和 `error`（无法处理文章）。要添加枚举，我们可以编写以下代码：

```
enum PostStatus {
  success
  pending
  error
}
```

完整架构可能如下所示：

```
schema {
  query: Query
  mutation: Mutation
}

type Post {
  id: ID!
  title: String
  date: AWSDateTime
  poststatus: PostStatus
}

type Mutation {
  addPost(id: ID!, title: String, date: AWSDateTime, poststatus: PostStatus): Post
}

type Query {
  getPosts: [Post]
}

enum PostStatus {  
  success
  pending
  error
}
```

如果用户在应用程序中添加 `Post`，将调用 `addPost` 操作以处理该数据。在附加到 `addPost` 的解析器处理数据时，它不断使用操作状态更新 `poststatus`。在查询时，`Post` 将包含数据的最终状态。请记住，我们只是介绍我们希望如何在架构中处理数据。我们对解析器实施进行了很多假设，这些解析器实施处理数据以完成请求的实际业务逻辑。

## 可选注意事项 - 订阅
<a name="optional-consideration-subscriptions"></a>

中的订阅 AWS AppSync 是作为对突变的响应而调用的。您可使用架构中的`Subscription` 类型和 `@aws_subscribe()` 指令进行配置，以指定哪些变更会调用一个或多个订阅。有关配置订阅的更多信息，请参阅[实时数据](https://docs.aws.amazon.com/appsync/latest/devguide/aws-appsync-real-time-data.html)。

## 可选的注意事项 - 关系和分页
<a name="optional-consideration-relations-and-pagination"></a>

假设您在 DynamoDB 表中存储了一百万个 `Posts`，并且您希望返回其中的一些数据。不过，上面给出的示例查询仅返回所有文章。您不希望每次发出请求时获取所有这些文章。相反，您可能希望对它们进行[分页](https://graphql.org/learn/pagination/)。请对您的架构进行以下改动：
+ 在 `getPosts` 字段中，添加两个输入参数：`nextToken`（迭代器）和 `limit`（迭代限制）。
+ 添加一个新的 `PostIterator` 类型，其中包含 `Posts`（检索 `Post` 对象列表）和 `nextToken`（迭代器）字段。
+ 更改 `getPosts` 以使其返回 `PostIterator`，而不是 `Post` 对象列表。

```
schema {
  query: Query
  mutation: Mutation
}

type Post {
  id: ID!
  title: String
  date: AWSDateTime
  poststatus: PostStatus
}

type Mutation {
  addPost(id: ID!, title: String, date: AWSDateTime, poststatus: PostStatus): Post
}

type Query {
  getPosts(limit: Int, nextToken: String): PostIterator
}

enum PostStatus {
  success
  pending
  error
}

type PostIterator {
  posts: [Post]
  nextToken: String
}
```

`PostIterator` 类型允许您返回 `Post` 对象列表的一部分，以及用于获取下一部分的 `nextToken`。在 `PostIterator` 中，具有一个 `Post` 项目 (`[Post]`) 列表，该列表与分页标记 (`nextToken`) 一起返回。在中 AWS AppSync，它将通过解析器连接到 Amazon DynamoDB，并自动生成为加密令牌。它会将 `limit` 参数的值转换为 `maxResults` 参数；并将 `nextToken` 参数转换为 `exclusiveStartKey` 参数。有关 AWS AppSync 控制台中的示例和内置模板示例，请参阅 [Resolver 参考 (JavaScript)](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-reference-js-version.html)。

# 将数据源附加到 AWS AppSync
<a name="attaching-a-data-source"></a>

数据源是 GraphQL APIs 可以与之交互的 AWS 账户中的资源。 AWS AppSync 支持多种数据源 AWS Lambda，例如亚马逊 DynamoDB、关系数据库（亚马逊 Aurora Serverless）、 OpenSearch 亚马逊服务和 HTTP 终端节点。可以将 AWS AppSync API 配置为与多个数据源进行交互，从而使您能够将数据聚合到一个位置。 AWS AppSync 可以使用您账户中的现有 AWS 资源或代表您通过架构定义配置 DynamoDB 表。

以下几节说明了如何将数据来源附加到 GraphQL API。

## 数据来源类型
<a name="data-source-types"></a>

现在，您已经在 AWS AppSync 控制台中创建了架构，可以将数据源附加到该架构。在您最初创建 API 时，可以选择在创建预定义的架构期间预置 Amazon DynamoDB 表。不过，我们不会在本节中介绍该选项。您可以在[启动架构](https://docs.aws.amazon.com//appsync/latest/devguide/schema-launch-start.html)一节中查看该选项的示例。

相反，我们将研究所有 AWS AppSync 支持的数据源。在为您的应用程序选择正确的解决方案时，需要考虑很多因素。以下几节为每个数据来源提供一些额外的上下文。有关数据来源的一般信息，请参阅[数据来源](https://docs.aws.amazon.com/appsync/latest/devguide/data-source-components.html)。

### Amazon DynamoDB
<a name="data-source-type-ddb"></a>

Amazon DynamoDB 是可扩展应用程序的 “主要” 存储解决方案之一 AWS。DynamoDB 的核心组件是**表**，它就是一个数据集合。您通常会根据 `Book` 或 `Author` 等实体创建表。表条目信息存储为**项目**，这些项目是每个条目的唯一字段组。完整的项目表示数据库 row/record 中的。例如，`Book` 条目的项目可能包括 `title` 和 `author` 及其值。像 `title` 和 `author` 这样的单独字段称为**属性**，它们类似于关系数据库中的列值。

你可以猜到，表将用于存储应用程序中的数据。 AWS AppSync 允许你将 DynamoDB 表连接到 GraphQL API 来操作数据。请从*前端 Web 和移动博客*中获取该[使用案例](https://aws.amazon.com/blogs/mobile/new-real-time-multi-group-app-with-aws-amplify-graphql-build-a-twitter-community-clone/)。该应用程序允许用户注册社交媒体应用程序。用户可以加入组，并上传文章以向订阅该组的其他用户广播。他们的应用程序将用户、文章和用户组信息存储在 DynamoDB 中。GraphQL API（由管理 AWS AppSync）与 DynamoDB 表连接。当用户在系统中进行更改并反映到前端时，GraphQL API 检索这些更改并向其他用户实时广播。

### AWS Lambda
<a name="data-source-type-lam"></a>

Lambda 是一种事件驱动的服务，它自动构建所需的资源以运行代码，从而响应事件。Lambda 使用**函数**，这些函数是包含用于执行资源的代码、依赖项和配置的组语句。在函数检测到**触发器**（一组调用函数的活动）时，将自动执行函数。触发器可以是应用程序发出 API 调用、账户中的 AWS 服务启动资源等。在触发时，函数将处理**事件**，这些事件是包含要修改的数据的 JSON 文档。

Lambda 非常适合运行代码，无需预置资源即可运行。请从*前端 Web 和移动博客*中获取该[使用案例](https://aws.amazon.com/blogs/mobile/building-a-graphql-api-with-java-and-aws-lambda/)。该使用案例与 DynamoDB 一节中说明的使用案例有点相似。在该应用程序中，GraphQL API 负责定义操作，例如，添加文章（变更）和获取该数据（查询）。为了实施其操作的功能（例如 `getPost ( id: String ! ) : Post`、`getPostsByAuthor ( author: String ! ) : [ Post ]`），它们使用 Lambda 函数处理入站请求。根据*选项 2： AWS AppSync 使用 Lambda 解析器*，他们使用该 AWS AppSync 服务来维护自己的架构，并将 Lambda 数据源链接到其中一个操作。在调用该操作时，Lambda 与 Amazon RDS 代理交互以对数据库执行业务逻辑。

### Amazon RDS
<a name="data-source-type-RDS"></a>

通过使用 Amazon RDS，您可以快速构建和配置关系数据库。在 Amazon RDS 中，您将创建一个通用**数据库实例**，以作为云中的隔离数据库环境。在该示例中，您使用一个**数据库引擎**，它是实际的 RDBMS 软件（PostgreSQL、MySQL 等）。该服务使用 AWS“基础架构”、修补和加密等安全服务提供可扩展性，从而减轻了大部分后端工作，并降低了部署的管理成本。

使用 Lambda 一节中的相同[使用案例](https://aws.amazon.com/blogs/mobile/building-a-graphql-api-with-java-and-aws-lambda/)。在*选项 3： AWS AppSync 使用亚马逊 RDS 解析器*下，提供的另一个选项是将 GraphQL API 直接链接 AWS AppSync 到亚马逊 RDS。通过使用[数据 API](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/data-api.html)，他们将数据库与 GraphQL API 相关联。一个解析器附加到字段（通常是查询、变更或订阅），并实施访问数据库所需的 SQL 语句。在客户端发出调用该字段的请求时，解析器执行这些语句并返回响应。

### Amazon EventBridge
<a name="data-source-type-eventbridge"></a>

在中 EventBridge，您将创建**事件总线**，这些管道接收来自您附加的服务或应用程序（**事件源**）的事件，并根据一组规则对其进行处理。**事件**是执行环境中的某种状态变化，而**规则**是事件的一组筛选条件。规则遵循一种**事件模式**或事件状态变化元数据（ID、区域、账号、ARN 等）。当事件与事件模式匹配时， EventBridge 将通过管道将事件发送到**目标服务（目标**），并触发规则中指定的操作。

EventBridge 非常适合将状态更改操作路由到其他服务。请从*前端 Web 和移动博客*中获取该[使用案例](https://aws.amazon.com/blogs/mobile/appsync-eventbridge/)。该示例介绍了一个电子商务解决方案，该解决方案具有多个团队以维护不同的服务。其中的一种服务在前端针对每个交付步骤（下订单、处理中、发货、交付等）向客户提供订单更新。不过，管理该服务的前端团队无法直接访问订购系统数据，因为该数据是由单独的后端团队维护的。后端团队的订购系统也被描述为黑匣子，因此，很难收集有关他们如何设置数据结构的信息。但是，后端团队确实建立了一个系统，该系统通过由管理的事件总线发布订单数据 EventBridge。为了访问来自事件总线的数据并将其路由到前端，前端团队创建了一个指向其GraphQL API的新目标。 AWS AppSync他们还创建一条规则，以仅发送与订单更新相关的数据。在进行更新时，来自该事件总线的数据将发送到 GraphQL API。API 中的架构处理数据，然后将其传送到前端。

### None 数据来源
<a name="data-source-type-none"></a>

如果不打算使用数据来源，您可以将其设置为 `none`。虽然 `none` 数据来源仍明确归类为数据来源，但并不是存储介质。通常，解析器在某一时刻调用一个或多个数据来源以处理请求。不过，在某些情况下，您可能不需要处理数据来源。如果将数据来源设置为 `none`，将运行请求，跳过数据调用步骤，然后运行响应。

从本 EventBridge 节中取同样的[用例](https://aws.amazon.com/blogs/mobile/appsync-eventbridge/)。在架构中，变更处理状态更新，然后将其发送给订阅者。回想一下解析器的工作方式，通常至少调用一次数据来源。不过，事件总线已自动发送本场景中的数据。这意味着变更不需要执行数据来源调用；可以直接在本地处理订单状态。变更设置为 `none`，它充当传递值而不会调用数据来源。然后，使用数据填充架构，该数据将发送给订阅者。

### OpenSearch
<a name="data-source-type-opensearch"></a>

Amazon S OpenSearch ervice 是一套用于实现全文搜索、数据可视化和日志记录的工具。可以使用该服务查询您上传的结构化数据。

在此服务中，您将创建的实例 OpenSearch。这些实例称为**节点**。在节点中，您添加至少一个**索引**。从概念上讲，索引有点像关系数据库中的表。（但是， OpenSearch 不符合 ACID，因此不应以这种方式使用）。您将使用上传到 OpenSearch 服务的数据填充索引。在上传您的数据时，将使用索引中存在的一个或多个分片对其编制索引。**分片**就像索引的一个分区，其中包含一些数据，并且可以与其他分片分开查询。在上传后，您的数据结构设置为称为**文档**的 JSON 文件。然后，您可以查询节点以获取文档中的数据。

### HTTP 端点
<a name="data-source-type-http"></a>

您可以使用 HTTP 端点作为数据源。 AWS AppSync 可以向端点发送包含参数和有效载荷等相关信息的请求。将向解析器公开 HTTP 响应，解析器在完成操作后返回最终响应。

## 添加数据来源
<a name="adding-a-data-source"></a>

如果您创建了数据源，则可以将其链接到 AWS AppSync 服务，更具体地说，可以链接到 API。

------
#### [ Console ]

1. 登录 AWS 管理控制台 并打开[AppSync控制台](https://console.aws.amazon.com/appsync/)。

   1. 在**控制面板**中选择您的 API。

   1. 在**侧边栏**中，选择**数据来源**。

1. 选择**创建数据来源**。

   1. 命名您的数据来源。您也可以为其提供描述，但这是可选的。

   1. 选择您的**数据来源类型**。

   1. 对于 DynamoDB，必须选择您的区域，然后选择该区域中的表。您可以选择创建新的通用表角色或导入表的现有角色，以规定与表交互的规则。您可以启用[版本控制](https://docs.aws.amazon.com/appsync/latest/devguide/conflict-detection-and-sync.html)，在多个客户端同时尝试更新数据时，该功能可以自动为每个请求创建数据版本。版本控制用于保留和维护多个数据变体，以实现冲突检测和解决目的。您还可以启用自动架构生成功能，该功能获取您的数据来源，并生成在架构中访问它所需的一些 CRUD、`List` 和 `Query` 操作。

      对于 OpenSearch，您必须选择您的区域，然后选择该区域中的域（集群）。您可以选择创建新的通用表角色或导入表的现有角色，以规定与域交互的规则。

      对于 Lambda，必须选择您的区域，然后选择该区域中的 Lambda 函数的 ARN。您可以选择创建新的通用表角色或导入表的现有角色，以规定与 Lambda 函数交互的规则。

      对于 HTTP，必须输入您的 HTTP 终端节点。

      对于 EventBridge，你必须选择你所在的地区，然后选择该地区的活动巴士。您可以选择创建新的通用表角色或导入表的现有角色，以规定与事件总线交互的规则。

      对于 RDS，必须选择您的区域，然后选择密钥存储（用户名和密码）、数据库名称和架构。

      对于“None”，您将添加一个没有实际数据来源的数据来源。这是为了在本地处理解析器，而不是通过实际数据来源。
**注意**  
如果要导入现有的角色，它们需要使用信任策略。有关更多信息，请参阅 [IAM 信任策略](#iam-trust-policy.title)。

1. 选择**创建**。
**注意**  
或者，如果要创建 DynamoDB 数据来源，您可以转到控制台中的**架构**页面，选择页面顶部的**创建资源**，然后填写一个预定义模型以转换为表。在该选项中，您填写或导入基本类型，配置包括分区键在内的基本表数据，并检查架构更改。

------
#### [ CLI ]
+ 运行 [https://docs.aws.amazon.com/cli/latest/reference/appsync/create-data-source.html](https://docs.aws.amazon.com/cli/latest/reference/appsync/create-data-source.html) 命令以创建数据来源。

  您需要为该特定命令输入一些参数：

  1. 您的 API 的 `api-id`。

  1. 您的表的 `name`。

  1. 数据来源的 `type`。根据您选择的数据来源类型，您可能需要输入 `service-role-arn` 和 `-config` 标签。

  示例命令可能如下所示：

  ```
   aws appsync create-data-source --api-id abcdefghijklmnopqrstuvwxyz --name data_source_name --type data_source_type --service-role-arn arn:aws:iam::107289374856:role/role_name --[data_source_type]-config {params}
  ```

------
#### [ CDK ]

**提示**  
在使用 CDK 之前，我们建议您查看 CDK 的[官方文档](https://docs.aws.amazon.com/cdk/v2/guide/home.html)以及 [CD](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_appsync-readme.html) K 参考资料。 AWS AppSync  
下面列出的步骤仅显示用于添加特定资源的一般代码片段示例。这**并不**意味着，它是您的生产代码中的有效解决方案。我们还假设您已具有正常工作的应用程序。

要添加您的特定数据来源，您需要将构造添加到堆栈文件中。可以在此处找到一个数据来源类型列表：
+  [ DynamoDbDataSource ](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_appsync.DynamoDbDataSource.html) 
+  [ EventBridgeDataSource ](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_appsync.EventBridgeDataSource.html) 
+  [ HttpDataSource ](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_appsync.HttpDataSource.html) 
+  [ LambdaDataSource ](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_appsync.LambdaDataSource.html) 
+  [ NoneDataSource ](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_appsync.NoneDataSource.html) 
+  [ OpenSearchDataSource ](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_appsync.OpenSearchDataSource.html) 
+  [ RdsDataSource ](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_appsync.RdsDataSource.html) 

1. 一般来说，您可能需要将 import 指令添加到您使用的服务中。例如，它可能采用以下形式：

   ```
   import * as x from 'x'; # import wildcard as the 'x' keyword from 'x-service'
   import {a, b, ...} from 'c'; # import {specific constructs} from 'c-service'
   ```

   例如，您可以通过以下方式导入 AWS AppSync 和 DynamoDB 服务：

   ```
   import * as appsync from 'aws-cdk-lib/aws-appsync';
   import * as dynamodb from 'aws-cdk-lib/aws-dynamodb';
   ```

1. 某些服务（例如 RDS）要求在创建数据来源之前在堆栈文件中进行一些额外的设置（例如，VPC 创建、角色和访问凭证）。有关更多信息，请参阅相关 CDK 页面中的示例。

1. 对于大多数数据源，尤其是 AWS 服务，您将在堆栈文件中创建数据源的新实例。通常，这会如下所示：

   ```
   const add_data_source_func = new service_scope.resource_name(scope: Construct, id: string, props: data_source_props);
   ```

   例如，以下是一个示例 Amazon DynamoDB 表：

   ```
   const add_ddb_table = new dynamodb.Table(this, 'Table_ID', {
     partitionKey: {
       name: 'id',
       type: dynamodb.AttributeType.STRING,
     },
     sortKey: {
       name: 'id',
       type: dynamodb.AttributeType.STRING,
     },
     tableClass: dynamodb.TableClass.STANDARD,
   });
   ```
**注意**  
大多数数据来源至少具有一个必需的属性（**不**使用 `?` 符号表示）。请参阅 CDK 文档以了解需要使用哪些属性。

1. 接下来，您需要将数据来源链接到 GraphQL API。建议的方法是，在为管道解析器创建函数时添加数据来源。例如，下面的代码片段是一个扫描 DynamoDB 表中的所有元素的函数：

   ```
   const add_func = new appsync.AppsyncFunction(this, 'func_ID', {
     name: 'func_name_in_console',
     add_api,
     dataSource: add_api.addDynamoDbDataSource('data_source_name_in_console', add_ddb_table),
     code: appsync.Code.fromInline(`
         export function request(ctx) {
           return { operation: 'Scan' };
         }
   
         export function response(ctx) {
           return ctx.result.items;
         }
     `),
     runtime: appsync.FunctionRuntime.JS_1_0_0,
   });
   ```

   在 `dataSource` 属性中，您可以调用 GraphQL API (`add_api`)，并使用其内置方法之一 (`addDynamoDbDataSource`) 在表和 GraphQL API 之间建立关联。参数是此链接的名称，该链接将存在于 AWS AppSync 控制台`data_source_name_in_console`中（在本例中）和表方法（`add_ddb_table`）。您在下一节中开始创建解析器，此时，将介绍有关该主题的更多信息。

   可以使用多种替代方法链接数据来源。从技术上讲，您可以将 `api` 添加到表函数的属性列表中。例如，以下是步骤 3 中的代码片段，但具有包含 GraphQL API 的 `api` 属性：

   ```
   const add_api = new appsync.GraphqlApi(this, 'API_ID', {
     ...
   });
   
   const add_ddb_table = new dynamodb.Table(this, 'Table_ID', {
   
    ...
   
     api: add_api
   });
   ```

   或者，您可以单独调用 `GraphqlApi` 构造：

   ```
   const add_api = new appsync.GraphqlApi(this, 'API_ID', {
     ...
   });
   
   const add_ddb_table = new dynamodb.Table(this, 'Table_ID', {
     ...
   });
   
   const link_data_source = add_api.addDynamoDbDataSource('data_source_name_in_console', add_ddb_table);
   ```

   我们建议仅在函数的属性中创建关联。否则，你要么必须在 AWS AppSync 控制台中手动将解析器函数链接到数据源（如果你想继续使用控制台值`data_source_name_in_console`），要么在函数中使用另一个名称创建单独的关联，比如`data_source_name_in_console_2`。这是由于属性处理信息的方式的限制造成的。
**注意**  
您必须重新部署应用程序才能看到更改。

------

### IAM 信任策略
<a name="iam-trust-policy"></a>

如果您为数据源使用现有 IAM 角色，则需要向该角色授予相应的权限，以便对您的 AWS 资源（例如在 Amazon DynamoDB 表`PutItem`上）执行操作。您还需要修改该角色的信任策略 AWS AppSync 以允许将其用于资源访问，如以下示例策略所示：

------
#### [ JSON ]

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
        "Effect": "Allow",
        "Principal": {
            "Service": "appsync.amazonaws.com"
        },
        "Action": "sts:AssumeRole"
        }
    ]
}
```

------

您也可以根据需要在信任策略中添加条件，以限制对数据来源的访问。目前，可以在这些条件中使用 `SourceArn` 和 `SourceAccount` 键。例如，以下策略仅限账户 `123456789012` 访问数据来源：

------
#### [ JSON ]

****  

```
{
  "Version":"2012-10-17",		 	 	 
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "appsync.amazonaws.com"
      },
      "Action": "sts:AssumeRole",
      "Condition": {
        "StringEquals": {
          "aws:SourceAccount": "123456789012"
        }
      }
    }
  ]
}
```

------

或者，您可以使用以下策略，仅限特定的 API（例如 `abcdefghijklmnopq`）访问数据来源：

------
#### [ JSON ]

****  

```
{
  "Version":"2012-10-17",		 	 	 
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "appsync.amazonaws.com"
      },
      "Action": "sts:AssumeRole",
      "Condition": {
        "ArnEquals": {
          "aws:SourceArn": "arn:aws:appsync:us-west-2:123456789012:apis/abcdefghijklmnopq"
        }
      }
    }
  ]
}
```

------

您可以使用以下策略将访问权限限制为 AWS AppSync APIs 来自特定区域的所有人`us-east-1`，例如：

------
#### [ JSON ]

****  

```
{
  "Version":"2012-10-17",		 	 	 
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "appsync.amazonaws.com"
      },
      "Action": "sts:AssumeRole",
      "Condition": {
        "ArnEquals": {
          "aws:SourceArn": "arn:aws:appsync:us-east-1:123456789012:apis/*"
        }
      }
    }
  ]
}
```

------

在下一节（[配置解析器](https://docs.aws.amazon.com//appsync/latest/devguide/resolver-config-overview.html)）中，我们将添加解析器业务逻辑，并将其附加到架构中的字段以处理数据来源中的数据。

有关角色策略配置的更多信息，请参阅《IAM 用户指南》中的[修改角色](https://docs.aws.amazon.com//IAM/latest/UserGuide/id_roles_manage_modify.html)。**

有关解析人员的跨账户访问权限的更多信息 AWS AppSync，请参阅为[构建跨账户 AWS LambdaAWS Lambda](https://aws.amazon.com/blogs/mobile/appsync-lambda-cross-account/)解析器。 AWS AppSync

# 在 AWS AppSync 中配置解析器
<a name="resolver-config-overview"></a>

在前面的章节中，您了解了如何创建 GraphQL 架构和数据来源，然后在 AWS AppSync 服务中将它们链接在一起。在您的架构中，您可能已在查询和变更中创建了一个或多个字段（操作）。虽然该架构描述了操作从数据来源请求的数据类型，但从未实施这些操作如何处理数据的行为。

操作的行为始终是在解析器中实施的，解析器将链接到执行操作的字段。有关解析器一般如何工作的更多信息，请参阅[解析器](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-components.html)页面。

在 AWS AppSync 中，解析器与运行时系统相关联，即，在其中执行解析器的环境。运行时系统决定了编写解析器时使用的语言。目前支持两种运行时系统：APPSYNC\$1JS (JavaScript) 和 Apache Velocity 模板语言 (VTL)。

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

该流程是一个管道解析器示例。在两个运行时系统中都支持管道解析器。不过，这仅简要说明了管道解析器的用途。此外，我们仅介绍一种可能的解析器配置。有关支持的解析器配置的更多信息，请参阅适用于 APPSYNC\$1JS 的 [JavaScript 解析器概述](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-reference-overview-js.html)或适用于 VTL 的[解析器映射模板概述](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-mapping-template-reference-overview.html)。

正如您看到的一样，解析器是模块化的。要使解析器的组件正常工作，它们必须能够从其他组件了解执行状态。从[解析器](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-components.html)一节中，您知道可以将有关执行状态的重要信息作为一组参数（`args`、`context` 等）传递给解析器中的每个组件。在 AWS AppSync 中，这是由 `context` 严格处理的。它是一个容器，用于存放有关解析的字段的信息。这可能包括传递的参数、结果、授权数据、标头数据等所有内容。有关上下文的更多信息，请参阅适用于 APPSYNC\$1JS 的[解析器上下文对象参考](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-context-reference-js.html)或适用于 VTL 的[解析器映射模板上下文参考](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-context-reference.html)。

上下文并不是可用于实施解析器的唯一工具。AWS AppSync 支持广泛的实用程序，用于值生成、错误处理、解析、转换等。您可以在[此处](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-util-reference-js.html)查看 APPSYNC\$1JS 的实用程序列表，或者在[此处](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-util-reference.html)查看 VTL 的实用程序列表。

在以下几节中，您将了解如何在 GraphQL API 中配置解析器。

**Topics**
+ [创建基本查询 (JavaScript)](configuring-resolvers-js.md)
+ [创建基本查询（VTL）](configuring-resolvers.md)

# 创建基本查询 (JavaScript)
<a name="configuring-resolvers-js"></a>

GraphQL 解析器将类型的架构中的字段连接到数据来源。解析器是用于完成请求的机制。

正在 AWS AppSync 使用解析器 JavaScript 将 GraphQL 表达式转换为数据源可以使用的格式。或者，可以使用 [Apache Velocity 模板语言 (VTL)](https://velocity.apache.org/engine/2.0/vtl-reference.html) 编写映射模板，以将 GraphQL 表达式转换为数据来源可使用的格式。

本节介绍如何使用 JavaScript配置解析器。解[析器教程 (JavaScript)](https://docs.aws.amazon.com/appsync/latest/devguide/tutorials-js.html) 部分提供了有关如何使用实现解析器的深入教程。 JavaScript[Resolver 参考 (JavaScript)](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-reference-js-version.html) 部分解释了可与解 JavaScript 析器一起使用的实用程序操作。

我们建议您在尝试使用任何上述教程之前先遵循本指南。

在本节中，我们将介绍如何为查询和变更创建和配置解析器。

**注意**  
本指南假设您已创建架构并至少具有一个查询或变更。如果您要获取订阅（实时数据），请参阅[本](https://docs.aws.amazon.com/appsync/latest/devguide/aws-appsync-real-time-data.html)指南。

在本节中，我们提供一些配置解析器的常规步骤以及一个使用以下架构的示例：

```
// schema.graphql file

input CreatePostInput {
  title: String
  date: AWSDateTime
}

type Post {
  id: ID!
  title: String
  date: AWSDateTime
}

type Mutation {
  createPost(input: CreatePostInput!): Post
}

type Query {
  getPost: [Post]
}
```

## 创建基本查询解析器
<a name="create-basic-query-resolver-js"></a>

本节说明了如何创建基本查询解析器。

------
#### [ Console ]

1. 登录 AWS 管理控制台 并打开[AppSync控制台](https://console.aws.amazon.com/appsync/)。

   1. 在**APIs 控制面板**中，选择你的 GraphQL API。

   1. 在**侧边栏**中，选择**架构**。

1. 输入架构和数据来源详细信息。有关更多信息，请参阅[设计架构](https://docs.aws.amazon.com/appsync/latest/devguide/designing-your-schema.html)和[附加数据来源](https://docs.aws.amazon.com/appsync/latest/devguide/attaching-a-data-source.html)小节。

1. 在**架构**编辑器旁边，具有一个名为**解析器**的窗口。该框包含**架构**窗口中定义的类型和字段列表。您可以将解析器附加到字段。您很可能会将解析器附加到字段操作。在本节中，我们将了解简单的查询配置。在 **Query** 类型下面，选择您的查询字段旁边的**附加**。

1. 在**附加解析器**页面上的**解析器类型**下面，您可以在管道解析器和单位解析器之间进行选择。有关这些类型的更多信息，请参阅[解析器](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-components.html)。本指南将使用`pipeline resolvers`。
**提示**  
在创建管道解析器时，您的数据来源将附加到管道函数。函数是在您创建管道解析器本身之后创建的，这就是为什么在该页面中没有设置数据来源的选项。如果您使用单位解析器，则数据来源直接绑定到解析器，因此，您可以在该页面中设置数据来源。

   对于 **Resolver 运行时**`APPSYNC_JS`，选择启用 JavaScript 运行时。

1. 您可以为该 API 启用[缓存](https://docs.aws.amazon.com/appsync/latest/devguide/enabling-caching.html)。我们建议暂时关闭该功能。选择**创建**。

1. 在**编辑解析器**页面上，具有一个名为**解析器代码**的代码编辑器，可用于实施解析器处理程序和响应的逻辑（预备步骤和后续步骤）。有关更多信息，请参阅[JavaScript解析器概述](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-reference-overview-js.html)。
**注意**  
在我们的示例中，我们直接将请求保留空白，并将响应设置为从[上下文](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-context-reference-js.html)返回最后的数据来源结果：  

   ```
   import {util} from '@aws-appsync/utils';
   
   export function request(ctx) {
       return {};
   }
   
   export function response(ctx) {
       return ctx.prev.result;
   }
   ```

   在该部分下面，具有有一个名为**函数**的表。函数用于实施可以在多个解析器中重复使用的代码。您可以将源代码存储为函数以在需要时添加到解析器中，而不是不断重新编写或复制代码。

   函数占据了管道的操作列表中的很大部分。在解析器中使用多个函数时，您可以设置函数顺序，将按该顺序运行它们。它们在请求函数运行之后和响应函数开始之前执行。

   要添加新函数，请在**函数**下面选择**添加函数**，然后选择**创建新函数**。或者，您可能会看到一个**创建函数**按钮可供选择。

   1. 选择一个数据来源。这是解析器处理的数据来源。
**注意**  
在我们的示例中，我们为 `getPost` 附加一个解析器，它按 `id` 检索 `Post` 对象。假设我们已为该架构设置一个 DynamoDB 表。其分区键设置为 `id` 并且为空。

   1. 输入一个`Function name`。

   1. 在**函数代码**下面，您需要实施函数的行为。这可能会令人困惑，但每个函数具有自己的本地请求和响应处理程序。先运行请求，然后调用数据来源以处理请求，最后由响应处理程序处理数据来源响应。结果存储在[上下文](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-context-reference-js.html)对象中。然后，运行列表中的下一个函数；如果是最后一个函数，则将其传递给后续步骤响应处理程序。
**注意**  
在我们的示例中，我们将一个解析器附加到 `getPost`，它从数据来源获取 `Post` 对象列表。我们的请求函数将从我们的表中请求数据，表将其响应传递给上下文 (ctx)，然后响应将在上下文中返回结果。 AWS AppSync的优势在于它与其他服务的互连性。 AWS 由于我们使用的是 DynamoDB，因此，我们具有[一组操作](https://docs.aws.amazon.com/appsync/latest/devguide/js-resolver-reference-dynamodb.html)以简化此类操作。我们还具有其他数据来源类型的一些样板示例。  
我们的代码将如下所示：  

      ```
      import { util } from '@aws-appsync/utils';
      
      /**
       * 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;
      }
      ```
在该步骤中，我们添加了两个函数：  
`request`：请求处理程序对数据来源执行检索操作。参数包含上下文对象 (`ctx`) 或为执行特定操作的所有解析器提供的一些数据。例如，它可能包含授权数据、解析的字段名称等。返回语句执行 [https://docs.aws.amazon.com//appsync/latest/devguide/js-resolver-reference-dynamodb.html#js-aws-appsync-resolver-reference-dynamodb-scan](https://docs.aws.amazon.com//appsync/latest/devguide/js-resolver-reference-dynamodb.html#js-aws-appsync-resolver-reference-dynamodb-scan) 操作（请参阅[此处](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Scan.html)的示例）。由于我们使用的是 DynamoDB，因此，我们可以使用该服务中的一些操作。扫描对表中的所有项目执行基本获取。该操作的结果作为 `result` 容器存储在上下文对象中，然后再传递给响应处理程序。`request` 在管道中的响应之前运行。
`response`：返回 `request` 输出的响应处理程序。参数是更新的上下文对象，返回语句是 `ctx.prev.result`。在本指南的当前阶段，您可能还不熟悉该值。`ctx` 指的是上下文对象。`prev` 指的是管道中的以前操作，也就是 `request`。`result` 包含在管道中执行解析器的结果。如果将它们放在一起，`ctx.prev.result` 将返回最后执行的操作的结果，即请求处理程序。

   1. 在完成后，选择**创建**。

1. 返回到解析器屏幕，在**函数**下面选择**添加函数**下拉列表，然后将您的函数添加到函数列表中。

1. 选择**保存**以更新解析器。

------
#### [ CLI ]

**添加您的函数**
+ 使用 `[create-function](https://docs.aws.amazon.com/cli/latest/reference/appsync/create-function.html)` 命令为管道解析器创建一个函数。

  您需要为该特定命令输入一些参数：

  1. 您的 API 的 `api-id`。

  1.  AWS AppSync 控制台`name`中该函数的。

  1. `data-source-name` 或函数使用的数据来源名称。它必须已创建并链接到 AWS AppSync 服务中的 GraphQL API。

  1. `runtime` 或函数的环境和语言。对于 JavaScript，名称必须为`APPSYNC_JS`，运行时必须为`1.0.0`。

  1. `code` 或函数的请求和响应处理程序。虽然您可以手动键入该内容，但将其添加到 .txt 文件（或类似格式）并作为参数传入要容易得多。
**注意**  
我们的查询代码将位于作为参数传入的文件中：  

     ```
     import { util } from '@aws-appsync/utils';
     
     /**
      * 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;
     }
     ```

  示例命令可能如下所示：

  ```
  aws appsync create-function \
  --api-id abcdefghijklmnopqrstuvwxyz \
  --name get_posts_func_1 \
  --data-source-name table-for-posts \
  --runtime name=APPSYNC_JS,runtimeVersion=1.0.0 \
  --code file://~/path/to/file/{filename}.{fileType}
  ```

  将在 CLI 中返回输出。示例如下：

  ```
  {
      "functionConfiguration": {
          "functionId": "ejglgvmcabdn7lx75ref4qeig4",
          "functionArn": "arn:aws:appsync:us-west-2:107289374856:apis/abcdefghijklmnopqrstuvwxyz/functions/ejglgvmcabdn7lx75ref4qeig4",
          "name": "get_posts_func_1",
          "dataSourceName": "table-for-posts",
          "maxBatchSize": 0,
          "runtime": {
              "name": "APPSYNC_JS",
              "runtimeVersion": "1.0.0"
          },
          "code": "Code output goes here"
      }
  }
  ```
**注意**  
确保将 `functionId` 记录在某处，因为它用于将函数附加到解析器。

**创建您的解析器**
+ 运行 `[create-resolver](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/appsync/create-resolver.html)` 命令，为 `Query` 创建一个管道函数。

  您需要为该特定命令输入一些参数：

  1. 您的 API 的 `api-id`。

  1. `type-name` 或架构中的特殊对象类型（查询、变更、订阅）。

  1. `field-name` 或要将解析器附加到的特殊对象类型中的字段操作。

  1. `kind`，它指定单位解析器或管道解析器。请将其设置为 `PIPELINE` 以启用管道函数。

  1. `pipeline-config` 或附加到解析器的函数。确保您知道函数的 `functionId` 值。列表顺序很重要。

  1. 那是`runtime`，那是 `APPSYNC_JS` (JavaScript)。`runtimeVersion` 目前是 `1.0.0`。

  1. `code`，它包含预备步骤和后续步骤处理程序。
**注意**  
我们的查询代码将位于作为参数传入的文件中：  

     ```
     import { util } from '@aws-appsync/utils';
     
     /**
      * Sends a request to `put` an item in the DynamoDB data source
      */
     export function request(ctx) {
       const { id, ...values } = ctx.args;
       return {
         operation: 'PutItem',
         key: util.dynamodb.toMapValues({ id }),
         attributeValues: util.dynamodb.toMapValues(values),
       };
     }
     
     /**
      * returns the result of the `put` operation
      */
     export function response(ctx) {
       return ctx.result;
     }
     ```

  示例命令可能如下所示：

  ```
  aws appsync create-resolver \
  --api-id abcdefghijklmnopqrstuvwxyz \
  --type-name Query \
  --field-name getPost \
  --kind PIPELINE \
  --pipeline-config functions=ejglgvmcabdn7lx75ref4qeig4 \
  --runtime name=APPSYNC_JS,runtimeVersion=1.0.0 \
  --code file:///path/to/file/{filename}.{fileType}
  ```

  将在 CLI 中返回输出。示例如下：

  ```
  {
      "resolver": {
          "typeName": "Mutation",
          "fieldName": "getPost",
          "resolverArn": "arn:aws:appsync:us-west-2:107289374856:apis/abcdefghijklmnopqrstuvwxyz/types/Mutation/resolvers/getPost",
          "kind": "PIPELINE",
          "pipelineConfig": {
              "functions": [
                  "ejglgvmcabdn7lx75ref4qeig4"
              ]
          },
          "maxBatchSize": 0,
          "runtime": {
              "name": "APPSYNC_JS",
              "runtimeVersion": "1.0.0"
          },
          "code": "Code output goes here"
      }
  }
  ```

------
#### [ CDK ]

**提示**  
在使用 CDK 之前，我们建议您查看 CDK 的[官方文档](https://docs.aws.amazon.com/cdk/v2/guide/home.html)以及 [CD](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_appsync-readme.html) K 参考资料。 AWS AppSync  
下面列出的步骤仅显示用于添加特定资源的一般代码片段示例。这**并不**意味着，它是您的生产代码中的有效解决方案。我们还假设您已具有正常工作的应用程序。

基本应用程序需要使用以下内容：

1. 服务导入指令

1. 架构代码

1. 数据来源生成器

1. 函数代码

1. 解析器代码

从[设计您的架构](https://docs.aws.amazon.com/appsync/latest/devguide/designing-your-schema.html)和[附加数据来源](https://docs.aws.amazon.com/appsync/latest/devguide/attaching-a-data-source.html)小节中，我们知道堆栈文件将包含以下格式的 import 指令：

```
import * as x from 'x'; # import wildcard as the 'x' keyword from 'x-service'
import {a, b, ...} from 'c'; # import {specific constructs} from 'c-service'
```

**注意**  
在前面的章节中，我们只说明了如何导入 AWS AppSync 构造。在实际代码中，您必须导入更多服务才能运行应用程序。在我们的示例中，如果我们要创建一个非常简单的 CDK 应用程序，我们至少要导入该 AWS AppSync 服务以及我们的数据源，即 DynamoDB 表。我们还需要导入一些额外的构造以部署应用程序：  

```
import * as cdk from 'aws-cdk-lib';
import * as appsync from 'aws-cdk-lib/aws-appsync';
import * as dynamodb from 'aws-cdk-lib/aws-dynamodb';
import { Construct } from 'constructs';
```
简要说明一下每个指令：  
`import * as cdk from 'aws-cdk-lib';`：用于定义 CDK 应用程序和构造，例如堆栈。它还包含一些对我们的应用程序非常有用的实用程序函数，例如处理元数据。如果您熟悉该 import 指令，但想知道为什么此处没有使用 cdk 核心库，请参阅 [Migration](https://docs.aws.amazon.com/cdk/v2/guide/migrating-v2.html) 页面。
`import * as appsync from 'aws-cdk-lib/aws-appsync';`：它导入 [AWS AppSync 服务](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_appsync-readme.html)。
`import * as dynamodb from 'aws-cdk-lib/aws-dynamodb';`：它导入 [DynamoDB 服务](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_dynamodb-readme.html)。
`import { Construct } from 'constructs';`：我们需要使用该指令以定义根[构造](https://docs.aws.amazon.com/cdk/v2/guide/constructs.html)。

导入类型取决于您调用的服务。我们建议查看 CDK 文档以获取示例。页面顶部的架构是 CDK 应用程序中的单独文件，显示为 `.graphql` 文件。在堆栈文件中，我们可以使用以下格式将其与新的 GraphQL 相关联：

```
const add_api = new appsync.GraphqlApi(this, 'graphQL-example', {
  name: 'my-first-api',
  schema: appsync.SchemaFile.fromAsset(path.join(__dirname, 'schema.graphql')),
});
```

**注意**  
在 `add_api` 范围内，我们使用 `new` 关键字和后面的 `appsync.GraphqlApi(scope: Construct, id: string , props: GraphqlApiProps)` 添加新的 GraphQL API。我们的范围是 `this`，CFN ID 是 `graphQL-example`，我们的属性是 `my-first-api`（控制台中的 API 的名称）和 `schema.graphql`（架构文件的绝对路径）。

要添加数据来源，您必须先将数据来源添加到堆栈中。然后，您需要使用源特定的方法将其与 GraphQL API 相关联。在您创建解析器函数时，将会发生关联。同时，让我们使用一个通过 `dynamodb.Table` 创建 DynamoDB 表的示例：

```
const add_ddb_table = new dynamodb.Table(this, 'posts-table', {
  partitionKey: {
    name: 'id',
    type: dynamodb.AttributeType.STRING,
  },
});
```

**注意**  
如果我们要在示例中使用该表，我们将添加一个 CFN ID 为 `posts-table` 且分区键为 `id (S)` 的新 DynamoDB 表。

接下来，我们需要在堆栈文件中实施解析器。以下是扫描 DynamoDB 表中的所有项目的简单查询示例：

```
const add_func = new appsync.AppsyncFunction(this, 'func-get-posts', {
  name: 'get_posts_func_1',
  add_api,
  dataSource: add_api.addDynamoDbDataSource('table-for-posts', add_ddb_table),
  code: appsync.Code.fromInline(`
      export function request(ctx) {
        return { operation: 'Scan' };
      }

      export function response(ctx) {
        return ctx.result.items;
      }
  `),
  runtime: appsync.FunctionRuntime.JS_1_0_0,
});

new appsync.Resolver(this, 'pipeline-resolver-get-posts', {
  add_api,
  typeName: 'Query',
  fieldName: 'getPost',
  code: appsync.Code.fromInline(`
      export function request(ctx) {
        return {};
      }

      export function response(ctx) {
        return ctx.prev.result;
      }
 `),
  runtime: appsync.FunctionRuntime.JS_1_0_0,
  pipelineConfig: [add_func],
});
```

**注意**  
首先，我们创建一个名为 `add_func` 的函数。这种创建顺序可能看起来有点违背常理，但您必须在创建管道解析器本身之前在解析器中创建函数。函数采用以下格式：  

```
AppsyncFunction(scope: Construct, id: string, props: AppsyncFunctionProps)
```
我们的范围是 `this`，CFN ID 是 `func-get-posts`，属性包含实际函数详细信息。我们在属性中包含：  
 AWS AppSync 控制台中将出现的函数 (`get_posts_func_1`)。`name`
我们以前创建的 GraphQL API (`add_api`)。
数据来源；我们在其中将数据来源链接到 GraphQL API 值，然后将其附加到函数。我们获取创建的表 (`add_ddb_table`)，并使用 `GraphqlApi` 方法之一 ([https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_appsync.GraphqlApi.html#addwbrdynamowbrdbwbrdatawbrsourceid-table-options](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_appsync.GraphqlApi.html#addwbrdynamowbrdbwbrdatawbrsourceid-table-options)) 将其附加到 GraphQL API (`add_api`)。ID 值 (`table-for-posts`) 是 AWS AppSync 控制台中的数据来源的名称。有关源特定的方法列表，请参阅以下页面：  
[ DynamoDbDataSource ](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_appsync.DynamoDbDataSource.html) 
 [ EventBridgeDataSource ](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_appsync.EventBridgeDataSource.html) 
 [ HttpDataSource ](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_appsync.HttpDataSource.html) 
 [ LambdaDataSource ](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_appsync.LambdaDataSource.html) 
 [ NoneDataSource ](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_appsync.NoneDataSource.html) 
 [ OpenSearchDataSource ](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_appsync.OpenSearchDataSource.html) 
 [ RdsDataSource ](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_appsync.RdsDataSource.html) 
代码包含函数的请求和响应处理程序，这是简单的扫描和返回。
运行时系统指定我们要使用 APPSYNC\$1JS 运行时系统 1.0.0 版。请注意，这是目前唯一适用于 APPSYNC\$1JS 的版本。
接下来，我们需要将函数附加到管道解析器。我们使用以下格式创建解析器：  

```
Resolver(scope: Construct, id: string, props: ResolverProps)
```
我们的范围是 `this`，CFN ID 是 `pipeline-resolver-get-posts`，属性包含实际函数详细信息。我们在属性中包含：  
我们以前创建的 GraphQL API (`add_api`)。
特殊对象类型名称；这是一个查询操作，因此，我们直接添加了 `Query` 值。
字段名称 (`getPost`) 是架构中的 `Query` 类型下面的字段名称。
代码包含预备步骤处理程序和后续步骤处理程序。我们的示例仅返回函数执行操作后在上下文中包含的任何结果。
运行时系统指定我们要使用 APPSYNC\$1JS 运行时系统 1.0.0 版。请注意，这是目前唯一适用于 APPSYNC\$1JS 的版本。
管道配置包含对我们创建的函数 (`add_func`) 的引用。

------

为了总结此示例中发生的情况，您看到了一个实现请求和响应处理程序的 AWS AppSync 函数。该函数负责与您的数据来源进行交互。请求处理程序向其发送了一个`Scan`操作 AWS AppSync，指示其对您的 DynamoDB 数据源执行什么操作。响应处理程序返回项目列表 (`ctx.result.items`)。然后，将项目列表自动映射到 `Post` GraphQL 类型。

## 创建基本变更解析器
<a name="creating-basic-mutation-resolvers-js"></a>

本节说明了如何创建基本变更解析器。

------
#### [ Console ]

1. 登录 AWS 管理控制台 并打开[AppSync控制台](https://console.aws.amazon.com/appsync/)。

   1. 在**APIs 控制面板**中，选择你的 GraphQL API。

   1. 在**侧边栏**中，选择**架构**。

1. 在**解析器**部分的 **Mutation** 类型下面，选择您的字段旁边的**附加**。
**注意**  
在我们的示例中，我们为 `createPost` 附加一个解析器，它将 `Post` 对象添加到我们的表中。假设我们使用上一节中的相同 DynamoDB 表。其分区键设置为 `id` 并且为空。

1. 在**附加解析器**页面上的**解析器类型**下面，选择`pipeline resolvers`。提醒一下，您可以在[此处](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-components.html)找到有关解析器的更多信息。对于 **Resolver 运行时**`APPSYNC_JS`，选择启用 JavaScript 运行时。

1. 您可以为该 API 启用[缓存](https://docs.aws.amazon.com/appsync/latest/devguide/enabling-caching.html)。我们建议暂时关闭该功能。选择**创建**。

1. 选择**添加函数**，然后选择**创建新函数**。或者，您可能会看到一个**创建函数**按钮可供选择。

   1. 选择您的 数据来源。这应该是使用变更处理数据的源。

   1. 输入一个`Function name`。

   1. 在**函数代码**下面，您需要实施函数的行为。这是一个变更，因此，理想情况下，请求将对调用的数据来源执行某种状态更改操作。结果由响应函数进行处理。
**注意**  
`createPost` 在表中添加或“放置”新的 `Post`，并将我们的参数作为数据。我们可能会添如下内容：  

      ```
      import { util } from '@aws-appsync/utils';
      
      /**
       * Sends a request to `put` an item in the DynamoDB data source
       */
      export function request(ctx) {
        return {
          operation: 'PutItem',
          key: util.dynamodb.toMapValues({id: util.autoId()}),
          attributeValues: util.dynamodb.toMapValues(ctx.args.input),
        };
      }
      
      /**
       * returns the result of the `put` operation
       */
      export function response(ctx) {
        return ctx.result;
      }
      ```
在该步骤中，我们还添加了 `request` 和 `response` 函数：  
`request`：请求处理程序接受上下文以作为参数。请求处理程序返回语句执行 [https://docs.aws.amazon.com//appsync/latest/devguide/js-resolver-reference-dynamodb.html#js-aws-appsync-resolver-reference-dynamodb-putitem](https://docs.aws.amazon.com//appsync/latest/devguide/js-resolver-reference-dynamodb.html#js-aws-appsync-resolver-reference-dynamodb-putitem) 命令，这是内置的 DynamoDB 操作（请参阅[此处](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/getting-started-step-2.html)或[此处](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/WorkingWithItems.html#WorkingWithItems.WritingData)的示例）。`PutItem` 命令获取分区 `key` 值（由 `util.autoid()` 自动生成）以及来自上下文参数输入的 `attributes`（这些是在请求中传递的值），以将 `Post` 对象添加到我们的 DynamoDB 表中。`key` 是 `id`，`attributes` 是 `date` 和 `title` 字段参数。它们通过 [https://docs.aws.amazon.com//appsync/latest/devguide/dynamodb-helpers-in-util-dynamodb-js.html#utility-helpers-in-toMap-js](https://docs.aws.amazon.com//appsync/latest/devguide/dynamodb-helpers-in-util-dynamodb-js.html#utility-helpers-in-toMap-js) 帮助程序预先设置格式以与 DynamoDB 表一起使用。
`response`：响应接受更新的上下文，并返回请求处理程序的结果。

   1. 在完成后，选择**创建**。

1. 返回到解析器屏幕，在**函数**下面选择**添加函数**下拉列表，然后将您的函数添加到函数列表中。

1. 选择**保存**以更新解析器。

------
#### [ CLI ]

**添加您的函数**
+ 使用 `[create-function](https://docs.aws.amazon.com/cli/latest/reference/appsync/create-function.html)` 命令为管道解析器创建一个函数。

  您需要为该特定命令输入一些参数：

  1. 您的 API 的 `api-id`。

  1.  AWS AppSync 控制台`name`中该函数的。

  1. `data-source-name` 或函数使用的数据来源名称。它必须已创建并链接到 AWS AppSync 服务中的 GraphQL API。

  1. `runtime` 或函数的环境和语言。对于 JavaScript，名称必须为`APPSYNC_JS`，运行时必须为`1.0.0`。

  1. `code` 或函数的请求和响应处理程序。虽然您可以手动键入该内容，但将其添加到 .txt 文件（或类似格式）并作为参数传入要容易得多。
**注意**  
我们的查询代码将位于作为参数传入的文件中：  

     ```
     import { util } from '@aws-appsync/utils';
     
     /**
      * Sends a request to `put` an item in the DynamoDB data source
      */
     export function request(ctx) {
       return {
         operation: 'PutItem',
         key: util.dynamodb.toMapValues({id: util.autoId()}),
         attributeValues: util.dynamodb.toMapValues(ctx.args.input),
       };
     }
     
     /**
      * returns the result of the `put` operation
      */
     export function response(ctx) {
       return ctx.result;
     }
     ```

  示例命令可能如下所示：

  ```
  aws appsync create-function \
  --api-id abcdefghijklmnopqrstuvwxyz \
  --name add_posts_func_1 \
  --data-source-name table-for-posts \
  --runtime name=APPSYNC_JS,runtimeVersion=1.0.0 \
  --code file:///path/to/file/{filename}.{fileType}
  ```

  将在 CLI 中返回输出。示例如下：

  ```
  {
      "functionConfiguration": {
          "functionId": "vulcmbfcxffiram63psb4dduoa",
          "functionArn": "arn:aws:appsync:us-west-2:107289374856:apis/abcdefghijklmnopqrstuvwxyz/functions/vulcmbfcxffiram63psb4dduoa",
          "name": "add_posts_func_1",
          "dataSourceName": "table-for-posts",
          "maxBatchSize": 0,
          "runtime": {
              "name": "APPSYNC_JS",
              "runtimeVersion": "1.0.0"
          },
          "code": "Code output foes here"
      }
  }
  ```
**注意**  
确保将 `functionId` 记录在某处，因为它用于将函数附加到解析器。

**创建您的解析器**
+ 运行 `[create-resolver](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/appsync/create-resolver.html)` 命令，为 `Mutation` 创建一个管道函数。

  您需要为该特定命令输入一些参数：

  1. 您的 API 的 `api-id`。

  1. `type-name` 或架构中的特殊对象类型（查询、变更、订阅）。

  1. `field-name` 或要将解析器附加到的特殊对象类型中的字段操作。

  1. `kind`，它指定单位解析器或管道解析器。请将其设置为 `PIPELINE` 以启用管道函数。

  1. `pipeline-config` 或附加到解析器的函数。确保您知道函数的 `functionId` 值。列表顺序很重要。

  1. 那是`runtime`，那是 `APPSYNC_JS` (JavaScript)。`runtimeVersion` 目前是 `1.0.0`。

  1. `code`，它包含预备步骤和后续步骤。
**注意**  
我们的查询代码将位于作为参数传入的文件中：  

     ```
     import { util } from '@aws-appsync/utils';
     
     /**
      * Sends a request to `put` an item in the DynamoDB data source
      */
     export function request(ctx) {
       const { id, ...values } = ctx.args;
       return {
         operation: 'PutItem',
         key: util.dynamodb.toMapValues({ id }),
         attributeValues: util.dynamodb.toMapValues(values),
       };
     }
     
     /**
      * returns the result of the `put` operation
      */
     export function response(ctx) {
       return ctx.result;
     }
     ```

  示例命令可能如下所示：

  ```
  aws appsync create-resolver \
  --api-id abcdefghijklmnopqrstuvwxyz \
  --type-name Mutation \
  --field-name createPost \
  --kind PIPELINE \
  --pipeline-config functions=vulcmbfcxffiram63psb4dduoa \
  --runtime name=APPSYNC_JS,runtimeVersion=1.0.0 \
  --code file:///path/to/file/{filename}.{fileType}
  ```

  将在 CLI 中返回输出。示例如下：

  ```
  {
      "resolver": {
          "typeName": "Mutation",
          "fieldName": "createPost",
          "resolverArn": "arn:aws:appsync:us-west-2:107289374856:apis/abcdefghijklmnopqrstuvwxyz/types/Mutation/resolvers/createPost",
          "kind": "PIPELINE",
          "pipelineConfig": {
              "functions": [
                  "vulcmbfcxffiram63psb4dduoa"
              ]
          },
          "maxBatchSize": 0,
          "runtime": {
              "name": "APPSYNC_JS",
              "runtimeVersion": "1.0.0"
          },
          "code": "Code output goes here"
      }
  }
  ```

------
#### [ CDK ]

**提示**  
在使用 CDK 之前，我们建议您查看 CDK 的[官方文档](https://docs.aws.amazon.com/cdk/v2/guide/home.html)以及 [CD](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_appsync-readme.html) K 参考资料。 AWS AppSync  
下面列出的步骤仅显示用于添加特定资源的一般代码片段示例。这**并不**意味着，它是您的生产代码中的有效解决方案。我们还假设您已具有正常工作的应用程序。
+ 要创建变更，假设您使用同一项目，您可以将其添加到堆栈文件中，就像查询一样。以下是修改的函数和解析器，用于在表中添加新 `Post` 的变更：

  ```
  const add_func_2 = new appsync.AppsyncFunction(this, 'func-add-post', {
    name: 'add_posts_func_1',
    add_api,
    dataSource: add_api.addDynamoDbDataSource('table-for-posts-2', add_ddb_table),
        code: appsync.Code.fromInline(`
            export function request(ctx) {
              return {
                operation: 'PutItem',
                key: util.dynamodb.toMapValues({id: util.autoId()}),
                attributeValues: util.dynamodb.toMapValues(ctx.args.input),
              };
            }
  
            export function response(ctx) {
              return ctx.result;
            }
        `), 
    runtime: appsync.FunctionRuntime.JS_1_0_0,
  });
  
  new appsync.Resolver(this, 'pipeline-resolver-create-posts', {
    add_api,
    typeName: 'Mutation',
    fieldName: 'createPost',
        code: appsync.Code.fromInline(`
            export function request(ctx) {
              return {};
            }
  
            export function response(ctx) {
              return ctx.prev.result;
            }
        `),
    runtime: appsync.FunctionRuntime.JS_1_0_0,
    pipelineConfig: [add_func_2],
  });
  ```
**注意**  
由于该变更和查询的结构相似，因此，我们仅介绍为创建变更而进行的更改。  
在该函数中，我们将 CFN ID 更改为 `func-add-post` 并将名称更改为 `add_posts_func_1`，以反映我们将 `Posts` 添加到表中的情况。在数据源中，我们在 AWS AppSync 控制台中与表 (`add_ddb_table`) 建立了新的关联，`table-for-posts-2`因为该`addDynamoDbDataSource`方法需要它。请记住，这个新的关联仍在使用我们之前创建的同一个表，但是我们现在在 AWS AppSync 控制台中有两个与它的连接：一个用于查询 as`table-for-posts`，另一个用于变异为`table-for-posts-2`。代码进行了更改以添加 `Post`，方法是自动生成其 `id` 值，并接受客户端输入以用于其余字段。  
在解析器中，我们将 id 值更改为 `pipeline-resolver-create-posts` 以反映我们将 `Posts` 添加到表中的情况。为了反映架构中的变更，类型名称更改为 `Mutation`，名称更改为 `createPost`。管道配置设置为我们的新变更函数 `add_func_2`。

------

为了总结本示例中发生的情况， AWS AppSync 自动将`createPost`字段中定义的参数从 GraphQL 架构转换为 DynamoDB 操作。该示例使用 `id` 键将记录存储在 DynamoDB 中，该键是使用我们的 `util.autoId()` 帮助程序自动创建的。您通过 AWS AppSync 控制台或其他方式发出的请求传递给上下文参数 (`ctx.args.input`) 的所有其他字段都将存储为表的属性。键和属性使用 `util.dynamodb.toMapValues(values)` 帮助程序自动映射到兼容的 DynamoDB 格式。

AWS AppSync 还支持用于编辑解析器的测试和调试工作流程。您可以在调用模板之前使用模拟 `context` 对象查看转换的模板值。或者，您可以选择在运行查询时以交互方式查看对数据来源的完整请求。有关更多信息，请参阅[测试和调试解析器 (JavaScript)](https://docs.aws.amazon.com/appsync/latest/devguide/test-debug-resolvers-js.html) 和[监控和日志记录](https://docs.aws.amazon.com/appsync/latest/devguide/monitoring.html#aws-appsync-monitoring)。

## 高级解析器
<a name="advanced-resolvers-js"></a>

如果您按照[设计您的架构](designing-your-schema.md#aws-appsync-designing-your-schema)中的可选分页一节进行操作，您仍然需要将解析器添加到请求才能使用分页。我们的示例使用名为 `getPosts` 的查询分页，每次仅返回一部分请求内容。该字段上的解析器代码可能如下所示：

```
/**
 * Performs a scan on the dynamodb data source
 */
export function request(ctx) {
  const { limit = 20, nextToken } = ctx.args;
  return { operation: 'Scan', limit, nextToken };
}

/**
 * @returns the result of the `put` operation
 */
export function response(ctx) {
  const { items: posts = [], nextToken } = ctx.result;
  return { posts, nextToken };
}
```

在请求中，我们传入请求的上下文。我们`limit`的 is*20*，这意味着我们在第一个查询`Posts`中最多返回 20。`nextToken` 光标固定在数据来源中的第一个 `Post` 条目。这些内容将传递给参数。然后，请求执行从第一个 `Post` 到扫描限制数的扫描。数据来源将结果存储在上下文中，该结果将传递给响应。响应返回它检索的 `Posts`，然后将 `nextToken` 设置为紧靠限制后面的 `Post` 条目。发出的下一个请求执行完全相同的操作，但从紧靠第一个查询后面的偏移开始。请记住，这些类型的请求是按顺序完成的，而不是并行完成的。

# 在 AWS AppSync () JavaScript 中测试和调试解析器
<a name="test-debug-resolvers-js"></a>

AWS AppSync 针对数据源对 GraphQL 字段执行解析器。在使用管道解析器时，函数与数据来源交互。如[JavaScript 解析器概述中所述](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-reference-overview-js.html)，函数通过使用写入运行时并在运行时运行的请求和响应处理程序与数据源进行通信。 JavaScript `APPSYNC_JS`这样，您就可以在与数据来源通信之前和之后提供自定义逻辑和条件。

为了帮助开发者编写、测试和调试这些解析器， AWS AppSync 控制台还提供了一些工具，用于创建 GraphQL 请求和响应，并将模拟数据传递给各个字段解析器。此外，您还可以在 AWS AppSync 控制台中执行查询、变更和订阅，并查看来自 Amazon CloudWatch 的整个请求的详细日志流。这包括来自数据来源的结果。

## 使用模拟数据进行测试
<a name="testing-with-mock-data-js"></a>

在调用 GraphQL 解析器时，它包含一个 `context` 对象，其中包含有关请求的相关信息。其中包括来自客户端的参数、身份信息以及 GraphQL 父字段的数据。它还存储来自数据来源的结果，可以在响应处理程序中使用这些结果。有关该结构以及编程时使用的可用帮助程序实用程序的更多信息，请参阅[解析器上下文对象参考](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-context-reference-js.html)。

在编写或编辑解析器函数时，您可以将*模拟* 或*测试上下文* 对象传递到控制台编辑器中。这样，您就可以了解请求和响应处理程序如何进行评估，而无需实际对数据来源运行。例如，您可以传递测试 `firstname: Shaggy` 参数，观察在您的模板代码中使用 `ctx.args.firstname` 时如何评估。您还可以测试任何实用程序帮助程序（例如 `util.autoId()` 或 `util.time.nowISO8601()`）的评估。

### 测试解析器
<a name="test-a-resolver-js"></a>

此示例将使用 AWS AppSync 控制台测试解析器。

1. 登录 AWS 管理控制台 并打开[AppSync控制台](https://console.aws.amazon.com/appsync/)。

   1. 在**APIs 控制面板**中，选择你的 GraphQL API。

   1. 在**侧边栏**中，选择**函数**。

1. 选择一个现有的函数。

1. 在**更新函数**页面顶部，选择**选择测试上下文**，然后选择**创建新上下文**。

1. 选择一个示例上下文对象，或者在下面的**配置测试上下文**窗口中手动填充 JSON。

1. 输入**测试上下文名称**。

1. 选择**保存**按钮。

1. 要使用此模拟上下文对象评估解析器，请选择 **Run Test (运行测试)**。

举一个更实际的例子，假设您具有一个存储 GraphQL 类型 `Dog` 的应用程序，该应用程序自动为对象生成 ID 并将其存储在 Amazon DynamoDB 中。您还希望通过 GraphQL 变更参数写入一些值，并仅允许特定用户查看响应。以下代码片段显示了架构的外观：

```
type Dog {
  breed: String
  color: String
}

type Mutation {
  addDog(firstname: String, age: Int): Dog
}
```

你可以编写一个 AWS AppSync 函数并将其添加到你的`addDog`解析器中来处理突变。要测试您的 AWS AppSync 函数，您可以填充上下文对象，如下例所示。以下代码拥有 `name` 和 `age` 客户端的参数，以及 `identity` 对象中填充的 `username`：

```
{
    "arguments" : {
        "firstname": "Shaggy",
        "age": 4
    },
    "source" : {},
    "result" : {
        "breed" : "Miniature Schnauzer",
        "color" : "black_grey"
    },
    "identity": {
        "sub" : "uuid",
        "issuer" : " https://cognito-idp.{region}.amazonaws.com/{userPoolId}",
        "username" : "Nadia",
        "claims" : { },
        "sourceIp" :[  "x.x.x.x" ],
        "defaultAuthStrategy" : "ALLOW"
    }
}
```

您可以使用以下代码测试您的 AWS AppSync函数：

```
import { util } from '@aws-appsync/utils';

export function request(ctx) {
  return {
    operation: 'PutItem',
    key: util.dynamodb.toMapValues({ id: util.autoId() }),
    attributeValues: util.dynamodb.toMapValues(ctx.args),
  };
}

export function response(ctx) {
  if (ctx.identity.username === 'Nadia') {
    console.log("This request is allowed")
    return ctx.result;
  }
  util.unauthorized();
}
```

评估的请求和响应处理程序具有来自测试上下文对象的数据以及从 `util.autoId()` 中生成的值。此外，如果您要将 `username` 更改为除 `Nadia` 之外的值，将不会返回结果，因为授权检查将失败。有关精细访问控制的更多信息，请参阅[授权使用案例](security-authorization-use-cases.md#aws-appsync-security-authorization-use-cases)。

### 使用's测试请求和响应处理 AWS AppSync程序 APIs
<a name="testing-with-appsync-api-js"></a>

您可以通过 `EvaluateCode` API 命令使用模拟数据远程测试您的代码。要开始使用该命令，请确保您已将 `appsync:evaluateMappingCode` 权限添加到您的策略中。例如：

------
#### [ JSON ]

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "appsync:evaluateCode",
            "Resource": "arn:aws:appsync:us-east-1:111122223333:*"
        }
    ]
}
```

------

您可以使用[AWS CLI](https://aws.amazon.com/cli/)或来利用该命令[AWS SDKs](https://aws.amazon.com/tools/)。例如，以上一节中的`Dog`架构及其 AWS AppSync 函数请求和响应处理程序为例。通过在本地工作站上使用 CLI，将代码保存到名为 `code.js` 的文件中，然后将 `context` 对象保存到名为 `context.json` 的文件中。从 Shell 中，运行以下命令：

```
$ aws appsync evaluate-code \
  --code file://code.js \
  --function response \
  --context file://context.json \
  --runtime name=APPSYNC_JS,runtimeVersion=1.0.0
```

响应包含一个 `evaluationResult`，其中包含处理程序返回的负载。它还包含一个 `logs` 对象，其中保存处理程序在评估期间生成的日志列表。这样，就可以轻松调试代码执行，并查看有关评估的信息以帮助排除故障。例如：

```
{
    "evaluationResult": "{\"breed\":\"Miniature Schnauzer\",\"color\":\"black_grey\"}",
    "logs": [
        "INFO - code.js:13:5: \"This request is allowed\""
    ]
}
```

可以将 `evaluationResult` 解析为 JSON，其中提供：

```
{
  "breed": "Miniature Schnauzer",
  "color": "black_grey"
}
```

通过使用 SDK，您可以轻松合并常用的测试套件中的测试以验证处理程序行为。我们建议使用 [Jest 测试框架](https://jestjs.io/)创建测试，但任何测试套件都有效。以下代码片段显示假设的验证运行。请注意，我们希望评估响应是有效的 JSON ，因此，我们使用 `JSON.parse` 从字符串响应中检索 JSON：

```
const AWS = require('aws-sdk')
const fs = require('fs')
const client = new AWS.AppSync({ region: 'us-east-2' })
const runtime = {name:'APPSYNC_JS',runtimeVersion:'1.0.0')

test('request correctly calls DynamoDB', async () => {
  const code = fs.readFileSync('./code.js', 'utf8')
  const context = fs.readFileSync('./context.json', 'utf8')
  const contextJSON = JSON.parse(context)
  
  const response = await client.evaluateCode({ code, context, runtime, function: 'request' }).promise()
  const result = JSON.parse(response.evaluationResult)
  
  expect(result.key.id.S).toBeDefined()
  expect(result.attributeValues.firstname.S).toEqual(contextJSON.arguments.firstname)
})
```

 这会产生以下结果：

```
Ran all test suites.
> jest

PASS ./index.test.js
✓ request correctly calls DynamoDB (543 ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 totalTime: 1.511 s, estimated 2 s
```

## 调试实时查询
<a name="debugging-a-live-query-js"></a>

除了调试生产应用程序的 end-to-end测试和日志记录之外，别无选择。 AWS AppSync 允许您使用 Amazon 记录错误和完整的请求详情 CloudWatch。此外，您可以使用 AWS AppSync 控制台测试 GraphQL 查询、突变和订阅，并将每个请求的实时日志数据流回查询编辑器进行实时调试。对于订阅而言，日志显示连接时间信息。

要执行此操作，您需要提前启用 Amazon CloudWatch 日志，如[监控和日志](monitoring.md#aws-appsync-monitoring)中所述。接下来，在 AWS AppSync 控制台中选择 “**查询**” 选项卡，然后输入有效的 GraphQL 查询。在右下部分中，单击并拖动**日志**窗口以打开“日志”视图。在页面顶部，选择“播放”箭头图标运行您的 GraphQL 查询。稍后，操作的完整请求和响应日志将流式传输到该部分，您可以在控制台中查看这些日志。

# 在 AWS AppSync () JavaScript 中配置和使用管道解析器
<a name="pipeline-resolvers-js"></a>

AWS AppSync 在 GraphQL 字段上执行解析器。在某些情况下，应用程序需要执行多个操作以解析单个 GraphQL 字段。通过使用管道解析器，开发人员现在可以编写称为“函数”的操作并按顺序执行它们。例如，管道解析器对于需要在获取字段数据之前执行授权检查的应用程序非常有用。

有关 JavaScript 管道解析器架构的更多信息，请参阅解析[JavaScript器](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-reference-overview-js.html#anatomy-of-a-pipeline-resolver-js)概述。

## 步骤 1：创建管线解析器
<a name="create-a-pipeline-resolver-js"></a>

在 AWS AppSync 控制台中，转到 “**架构**” 页面。

保存以下架构：

```
schema {
    query: Query
    mutation: Mutation
}

type Mutation {
    signUp(input: Signup): User
}

type Query {
    getUser(id: ID!): User
}

input Signup {
    username: String!
    email: String!
}

type User {
    id: ID!
    username: String
    email: AWSEmail
}
```

我们将一个管道解析器连接到 **Mutation** 类型的 **signUp** 字段。在右侧的 **Mutation** 类型中，选择 `signUp` 变更字段旁边的**附加**。将解析器设置为`pipeline resolver`和 `APPSYNC_JS` 运行时系统，然后创建解析器。

我们的管道解析器通过先验证电子邮件地址输入，然后将用户保存在系统中来注册用户。我们将电子邮件验证封装在 **validateEmail** 函数中，并将用户保存在 **saveUser** 函数中。首先执行 **validateEmail** 函数，如果电子邮件有效，则执行 **saveUser** 函数。

执行流程如下所示：

1. Mutation.signUp 解析器请求处理程序

1. validateEmail 函数

1. saveUser 函数

1. Mutation.signUp 解析器响应处理程序

由于我们可能在 API 上的其他解析器中重复使用 **validateEmail** 函数，因此，我们希望避免访问 `ctx.args`，因为它们将从一个 GraphQL 字段更改为另一个字段。相反，我们可以使用 `ctx.stash` 存储 `signUp(input: Signup)` 输入字段参数中的电子邮件属性。

替换请求函数和响应函数以更新解析器代码：

```
export function request(ctx) {
    ctx.stash.email = ctx.args.input.email
    return {};
}

export function response(ctx) {
    return ctx.prev.result;
}
```

选择**创建**或**保存**以更新解析器。

## 步骤 2：创建函数
<a name="create-a-function-js"></a>

从管道解析器页面的**函数**部分中，单击**添加函数**，然后单击**创建新函数**。也可以在不通过解析器页面的情况下创建函数；要执行此操作，请在 AWS AppSync控制台中转到 “**函数**” 页面。选择**创建函数**按钮。让我们创建一个函数来检查电子邮件是否有效并来自特定域。如果电子邮件无效，该函数会引发一个错误。否则，它将转发接收到的任何输入。

确保您已创建一个 **NONE** 类型的数据来源。在**数据来源名称**列表中选择该数据来源。对于**函数名称**，请输入 `validateEmail`。在**函数代码**区域中，使用以下代码片段覆盖所有内容：

```
import { util } from '@aws-appsync/utils';

export function request(ctx) {
  const { email } = ctx.stash;
  const valid = util.matches(
    '^[a-zA-Z0-9_.+-]+@(?:(?:[a-zA-Z0-9-]+\.)?[a-zA-Z]+\.)?(myvaliddomain)\.com',
    email
  );
  if (!valid) {
    util.error(`"${email}" is not a valid email.`);
  }

  return { payload: { email } };
}

export function response(ctx) {
  return ctx.result;
}
```

检查您的输入，然后选择**创建**。我们刚刚创建了 **validateEmail** 函数。重复这些步骤以创建具有以下代码的 **saveUser** 函数（为了简单起见，我们使用 **NONE** 数据来源，并假设在函数执行后已将用户保存在系统中）：

```
import { util } from '@aws-appsync/utils';

export function request(ctx) {
  return ctx.prev.result;
}

export function response(ctx) {
  ctx.result.id = util.autoId();
  return ctx.result;
}
```

我们刚刚创建了 **saveUser** 函数。

## 步骤 3：将函数添加到管线解析器
<a name="adding-a-function-to-a-pipeline-resolver-js"></a>

我们的函数应该已自动添加到刚刚创建的管道解析器中。如果情况并非如此，或者您通过**函数**页面创建了函数，您可以再次单击 `signUp` 解析器页面上的**添加函数**以附加这些函数。将 **validateEmail** 和 **saveUser** 函数添加到解析器中。**validateEmail** 函数应放在 **saveUser** 函数之前。在添加更多函数时，您可以使用**上移**和**下移**选项重新排列函数的执行顺序。检查您的更改，然后选择**保存**。

## 步骤 4：运行查询
<a name="running-a-query-js"></a>

在 AWS AppSync 控制台中，转到 “**查询**” 页面。在资源管理器中，确保您正在使用变更。如果不是，请在下拉列表中选择`Mutation`，然后选择 `+`。输入以下查询：

```
mutation {
  signUp(input: {email: "nadia@myvaliddomain.com", username: "nadia"}) {
    id
    username
  }
}
```

它应返回如下内容：

```
{
  "data": {
    "signUp": {
      "id": "256b6cc2-4694-46f4-a55e-8cb14cc5d7fc",
      "username": "nadia"
    }
  }
}
```

我们已成功注册用户，并使用管道解析器验证了输入电子邮件。

# 创建基本查询（VTL）
<a name="configuring-resolvers"></a>

**注意**  
我们现在主要支持 APPSYNC\$1JS 运行时系统及其文档。请考虑使用 APPSYNC\$1JS 运行时系统和[此处](https://docs.aws.amazon.com/appsync/latest/devguide/configuring-resolvers-js.html)的指南。

GraphQL 解析器将类型的架构中的字段连接到数据来源。解析器是满足请求的机制。 AWS AppSync 无需编写任何代码，即可自动创建和连接架构中的解析器，或者创建架构并从现有表连接解析器。

 AWS AppSync 用于将 GraphQL 表达式 JavaScript 转换为数据源可以使用的格式的解析器。或者，可以使用 [Apache Velocity 模板语言 (VTL)](https://velocity.apache.org/engine/2.0/vtl-reference.html) 编写映射模板，以将 GraphQL 表达式转换为数据来源可使用的格式。

本节说明了如何使用 VTL 配置解析器。关于编写解析器的入门教程式编程指南可以在解析器[映射模板编程指南中找到，编程](resolver-mapping-template-reference-programming-guide.md#aws-appsync-resolver-mapping-template-reference-programming-guide)时可用的帮助工具可以在[解析器](resolver-context-reference.md#aws-appsync-resolver-mapping-template-context-reference)映射模板上下文参考中找到。 AWS AppSync 还具有内置的测试和调试流程，您可以在从头开始编辑或创作时使用这些流程。有关更多信息，请参阅[测试和调试解析器](test-debug-resolvers.md#aws-appsync-test-debug-resolvers)。

我们建议您在尝试使用任何上述教程之前先遵循本指南。

在本节中，我们将介绍如何创建解析器，为变更添加解析器以及使用高级配置。

## 创建您的第一个解析器
<a name="create-your-first-resolver"></a>

按照前面几节中的示例，第一步是为 `Query` 类型创建一个解析器。

------
#### [ Console ]

1. 登录 AWS 管理控制台 并打开[AppSync 控制台](https://console.aws.amazon.com/appsync/)。

   1. 在**APIs 控制面板**中，选择你的 GraphQL API。

   1. 在**侧边栏**中，选择**架构**。

1. 在页面右侧，具有一个名为**解析器**的窗口。该框包含页面左侧的**架构**窗口中定义的类型和字段列表。您可以将解析器附加到字段。例如，在 **Query** 类型下面，选择 `getTodos` 字段旁边的**附加**。

1. 在**创建解析器**页面上，选择您在[附加数据来源](https://docs.aws.amazon.com/appsync/latest/devguide/attaching-a-data-source.html)指南中创建的数据来源。在**配置映射模板**窗口中，您可以使用右侧的下拉列表选择通用请求和响应映射模板，也可以编写自己的映射模板。
**注意**  
请求映射模板与响应映射模板的配对称为单位解析器。单位解析器通常用于执行机械性的操作；我们建议仅将它们用于具有少量数据来源的单一操作。对于更复杂的操作，我们建议使用管道解析器，该解析器按顺序对多个数据来源执行多个操作。  
有关请求映射模板和响应映射模板之间的差异的更多信息，请参阅[单位解析器](https://docs.aws.amazon.com//appsync/latest/devguide/resolver-mapping-template-reference-overview.html#unit-resolvers)。  
有关使用管道解析器的更多信息，请参阅[管道解析器](pipeline-resolvers.md#aws-appsync-pipeline-resolvers)。

1. 对于常见用例， AWS AppSync 控制台内置了模板，您可以使用这些模板从数据源获取项目（例如，所有项目查询、个人查询等）。例如，对于[设计您的架构](designing-your-schema.md#aws-appsync-designing-your-schema)中的简单架构版本（`getTodos` 没有分页），用于列出项目的请求映射模板如下所示：

   ```
   {
       "version" : "2017-02-28",
       "operation" : "Scan"
   }
   ```

1. 您始终需要为请求提供响应映射模板。控制台为列表提供的默认模板具有以下传递值：

   ```
   $util.toJson($ctx.result.items)
   ```

   此示例中，项目列表的 `context` 对象（别名为 `$ctx`）的格式为 `$context.result.items`。如果您的 GraphQL 操作返回一个项目，它将是 `$context.result`。 AWS AppSync 为常用操作提供帮助程序函数，如之前列出的 `$util.toJson` 函数，以保证响应格式正确。有关完整的函数列表，请参阅[解析器映射模板实用程序参考](resolver-util-reference.md#aws-appsync-resolver-mapping-template-util-reference)。

1. 选择**保存解析器**。

------
#### [ API ]

1. 调用 [https://docs.aws.amazon.com/appsync/latest/APIReference/API_CreateResolver.html](https://docs.aws.amazon.com/appsync/latest/APIReference/API_CreateResolver.html) API 以创建一个解析器对象。

1. 您可以调用 [https://docs.aws.amazon.com/appsync/latest/APIReference/API_UpdateResolver.html](https://docs.aws.amazon.com/appsync/latest/APIReference/API_UpdateResolver.html) API 以修改解析器的字段。

------
#### [ CLI ]

1. 运行 [https://awscli.amazonaws.com/v2/documentation/api/latest/reference/appsync/create-resolver.html](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/appsync/create-resolver.html) 命令以创建解析器。

   您需要为该特定命令键入 6 个参数：

   1. 您的 API 的 `api-id`。

   1. 您要在架构中修改的类型的 `type-name`。在控制台示例中，这是 `Query`。

   1. 您要在类型中修改的字段的 `field-name`。在控制台示例中，这是 `getTodos`。

   1. 您在[附加数据来源](https://docs.aws.amazon.com/appsync/latest/devguide/attaching-a-data-source.html)指南中创建的数据来源的 `data-source-name`。

   1. `request-mapping-template`，这是请求的正文。在控制台示例中，这是：

      ```
      {
          "version" : "2017-02-28",
          "operation" : "Scan"
      }
      ```

   1. `response-mapping-template`，这是响应的正文。在控制台示例中，这是：

      ```
      $util.toJson($ctx.result.items)
      ```

   示例命令可能如下所示：

   ```
   aws appsync create-resolver --api-id abcdefghijklmnopqrstuvwxyz --type-name Query --field-name getTodos --data-source-name TodoTable --request-mapping-template "{ "version" : "2017-02-28", "operation" : "Scan", }" --response-mapping-template ""$"util.toJson("$"ctx.result.items)"
   ```

   将在 CLI 中返回输出。示例如下：

   ```
   {
       "resolver": {
           "kind": "UNIT",
           "dataSourceName": "TodoTable",
           "requestMappingTemplate": "{ version : 2017-02-28, operation : Scan, }",
           "resolverArn": "arn:aws:appsync:us-west-2:107289374856:apis/abcdefghijklmnopqrstuvwxyz/types/Query/resolvers/getTodos",
           "typeName": "Query",
           "fieldName": "getTodos",
           "responseMappingTemplate": "$util.toJson($ctx.result.items)"
       }
   }
   ```

1. 要修改解析器的字段 and/or 映射模板，请运行[https://awscli.amazonaws.com/v2/documentation/api/latest/reference/appsync/update-resolver.html](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/appsync/update-resolver.html)命令。

   除了 `api-id` 参数以外，`create-resolver` 命令中使用的参数将由 `update-resolver` 命令中的新值覆盖。

------

## 为变更添加解析器
<a name="adding-a-resolver-for-mutations"></a>

下一步是为您的 `Mutation` 类型创建一个解析器。

------
#### [ Console ]

1. 登录 AWS 管理控制台 并打开[AppSync 控制台](https://console.aws.amazon.com/appsync/)。

   1. 在**APIs 控制面板**中，选择你的 GraphQL API。

   1. 在**侧边栏**中，选择**架构**。

1. 在 **Mutation** 类型下面，选择 `addTodo` 字段旁边的**附加**。

1. 在**创建解析器**页面上，选择您在[附加数据来源](https://docs.aws.amazon.com/appsync/latest/devguide/attaching-a-data-source.html)指南中创建的数据来源。

1. 在**配置映射模板**窗口中，您需要修改请求模板，因为这是一个变更，该操作将新项目添加 DynamoDB 中。使用以下请求映射模板：

   ```
   {
       "version" : "2017-02-28",
       "operation" : "PutItem",
       "key" : {
           "id" : $util.dynamodb.toDynamoDBJson($ctx.args.id)
       },
       "attributeValues" : $util.dynamodb.toMapValuesJson($ctx.args)
   }
   ```

1. AWS AppSync 自动将`addTodo`字段中定义的参数从 GraphQL 架构转换为 DynamoDB 操作。上一示例使用 `id` 键将记录存储在 DynamoDB 中，该键是从变更参数中作为 `$ctx.args.id` 传递的。您传递的所有其他字段使用 `$util.dynamodb.toMapValuesJson($ctx.args)` 自动映射到 DynamoDB 属性。

   对于此解析器，使用以下响应映射模板：

   ```
   $util.toJson($ctx.result)
   ```

   AWS AppSync 还支持用于编辑解析器的测试和调试工作流程。您可在调用之前使用模拟 `context` 对象查看模板经过转换的值。还可在运行查询时选择以交互方式查看对数据来源的完整请求执行过程。有关更多信息，请参阅[测试和调试解析器](test-debug-resolvers.md#aws-appsync-test-debug-resolvers)和[监控和日志记录](monitoring.md#aws-appsync-monitoring)。

1. 选择**保存解析器**。

------
#### [ API ]

您也可以使用 “[创建您的第一个解析器](https://docs.aws.amazon.com/appsync/latest/devguide/configuring-resolvers.html#create-your-first-resolver)” 部分中的命令以及本节中的参数详细信息来执行此操作。 APIs 

------
#### [ CLI ]

您也可以在 CLI 中使用[创建您的第一个解析器](https://docs.aws.amazon.com/appsync/latest/devguide/configuring-resolvers.html#create-your-first-resolver)一节中的命令以及本节中的参数详细信息执行该操作。

------

此时，如果您不使用高级解析器，您可以开始使用 GraphQL API，如[使用 API](using-your-api.md#aws-appsync-using-your-api) 中所述。

## 高级解析器
<a name="advanced-resolvers"></a>

如果您按照[设计您的架构](designing-your-schema.md#aws-appsync-designing-your-schema)的“高级”一节进行操作，并构建一个示例架构以执行分页扫描，请将以下请求模板用于 `getTodos` 字段：

```
{
    "version" : "2017-02-28",
    "operation" : "Scan",
    "limit": $util.defaultIfNull(${ctx.args.limit}, 20),
    "nextToken": $util.toJson($util.defaultIfNullOrBlank($ctx.args.nextToken, null))
}
```

对于这个分页使用案例，响应映射不只是简单的传递，因为它必须包含*光标*（这样客户端才知道下次从哪一页开始）以及结果集。映射模板如下所示：

```
{
    "todos": $util.toJson($context.result.items),
    "nextToken": $util.toJson($context.result.nextToken)
}
```

以上响应映射模板中的字段必须与 `TodoConnection` 类型中定义的字段匹配。

如果存在以下关系：您具有 `Comments` 表并且要解析 `Todo` 类型（返回 `[Comment]` 类型）的 Comments 字段，您可以使用映射模板以对第二个表运行查询。为此，您必须已为 `Comments` 表创建了一个数据来源，如[附加数据来源](attaching-a-data-source.md#aws-appsync-getting-started-build-a-schema-from-scratch)中所述。

**注意**  
我们对第二个表使用查询操作仅用于说明目的。您可以对 DynamoDB 使用其他操作。此外，您可以从其他数据源（例如 AWS Lambda 或 Amazon S OpenSearch ervice）提取数据，因为该关系由您的 GraphQL 架构控制。

------
#### [ Console ]

1. 登录 AWS 管理控制台 并打开[AppSync 控制台](https://console.aws.amazon.com/appsync/)。

   1. 在**APIs 控制面板**中，选择你的 GraphQL API。

   1. 在**侧边栏**中，选择**架构**。

1. 在 **Todo** 类型下面，选择 `comments` 字段旁边的**附加**。

1. 在**创建解析器**页面上，选择您的 **Comments** 表数据来源。快速入门指南中的 **Comments** 表的默认名称是 `AppSyncCommentTable`，但它可能会根据您指定的名称而有所不同。

1. 将以下代码片段添加到您的请求映射模板中：

   ```
   {
       "version": "2017-02-28",
       "operation": "Query",
       "index": "todoid-index",
       "query": {
           "expression": "todoid = :todoid",
           "expressionValues": {
               ":todoid": {
                   "S": $util.toJson($context.source.id)
               }
           }
       }
   }
   ```

1. `context.source` 会引用当前被解析的字段的父对象。在本示例中，`source.id` 指单个 `Todo` 对象，之后它会被用于查询表达式。

   您可以如下所示使用传递响应映射模板：

   ```
   $util.toJson($ctx.result.items)
   ```

1. 选择**保存解析器**。

1. 最后，返回到控制台中的**架构**页面，将一个解析器附加到 `addComment` 字段，并指定 `Comments` 表的数据来源。在此例中请求映射模板只是简单的 `PutItem`，它具有作为参数注释的特定 `todoid`，但您可以使用 `$utils.autoId()` 实用程序来为如下所示注释创建唯一的排序键：

   ```
   {
       "version": "2017-02-28",
       "operation": "PutItem",
       "key": {
           "todoid": { "S": $util.toJson($context.arguments.todoid) },
           "commentid": { "S": "$util.autoId()" }
       },
       "attributeValues" : $util.dynamodb.toMapValuesJson($ctx.args)
   }
   ```

   如下所示使用传递响应模板：

   ```
   $util.toJson($ctx.result)
   ```

------
#### [ API ]

您也可以使用 “[创建您的第一个解析器](https://docs.aws.amazon.com/appsync/latest/devguide/configuring-resolvers.html#create-your-first-resolver)” 部分中的命令以及本节中的参数详细信息来执行此操作。 APIs 

------
#### [ CLI ]

您也可以在 CLI 中使用[创建您的第一个解析器](https://docs.aws.amazon.com/appsync/latest/devguide/configuring-resolvers.html#create-your-first-resolver)一节中的命令以及本节中的参数详细信息执行该操作。

------

# 使用直接 Lambda 解析器（VTL）禁用 VTL 映射模板
<a name="direct-lambda-reference"></a>

**注意**  
我们现在主要支持 APPSYNC\$1JS 运行时系统及其文档。请考虑使用 APPSYNC\$1JS 运行时系统和[此处](https://docs.aws.amazon.com/appsync/latest/devguide/configuring-resolvers-js.html)的指南。

使用直接 Lambda 解析器，您可以在使用数据源时避开 VTL 映射模板的使用。 AWS Lambda AWS AppSync 可以为您的 Lambda 函数提供默认负载，以及从 Lambda 函数的响应到 GraphQL 类型的默认转换。您可以选择提供请求模板、响应模板，或者两者都不提供， AWS AppSync 并将相应地进行处理。

要详细了解 AWS AppSync 提供的默认请求负载和响应转换，请参阅 [Direct Lambda 解析器](resolver-mapping-template-reference-lambda.md#direct-lambda-resolvers)参考。有关设置 AWS Lambda 数据源和设置 IAM 信任策略的更多信息，请参阅[附加数据源](attaching-a-data-source.md)。

## 配置直接 Lambda 解析器
<a name="direct-lambda-reference-resolvers"></a>

以下几节说明了如何附加 Lambda 数据来源，并将 Lambda 解析器添加到您的字段中。

### 添加 Lambda 数据来源
<a name="direct-lambda-datasource"></a>

您必须先添加 Lambda 数据来源，然后才能激活直接 Lambda 解析器。

------
#### [ Console ]

1. 登录 AWS 管理控制台 并打开[AppSync控制台](https://console.aws.amazon.com/appsync/)。

   1. 在**APIs 控制面板**中，选择你的 GraphQL API。

   1. 在**侧边栏**中，选择**数据来源**。

1. 选择**创建数据来源**。

   1. 对于**数据来源名称**，输入您的数据来源的名称，例如 **myFunction**。

   1. 对于**数据来源类型**，选择 **AWS Lambda 函数**。

   1. 对于**区域**，选择相应的区域。

   1. 对于**函数 ARN**，从下拉列表中选择 Lambda 函数。您可以搜索函数名称，或手动输入要使用的函数的 ARN。

   1. 创建新的 IAM 角色（建议），或者选择具有 `lambda:invokeFunction` IAM 权限的现有角色。现有角色需要具有一个信任策略，如[附加数据来源](attaching-a-data-source.md)一节中所述。

      以下是一个示例 IAM 策略，该策略具有对资源执行操作所需的权限：

------
#### [ JSON ]

****  

      ```
      { 
           "Version":"2012-10-17",		 	 	  
           "Statement": [ 
               { 
                   "Effect": "Allow", 
                   "Action": [ "lambda:invokeFunction" ], 
                   "Resource": [ 
                       "arn:aws:lambda:us-west-2:123456789012:function:myFunction", 
                       "arn:aws:lambda:us-west-2:123456789012:function:myFunction:*" 
                   ] 
               } 
           ] 
       }
      ```

------

1. 选择**创建**按钮。

------
#### [ CLI ]

1. 运行 [https://awscli.amazonaws.com/v2/documentation/api/latest/reference/appsync/create-data-source.html](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/appsync/create-data-source.html) 命令以创建数据来源对象。

   您需要为该特定命令键入 4 个参数：

   1. 您的 API 的 `api-id`。

   1. 您的数据来源的 `name`。在控制台示例中，这是**数据来源名称**。

   1. 数据来源的 `type`。在控制台示例中，这是 **AWS Lambda 函数**。

   1. `lambda-config`，即控制台示例中的**函数 ARN**。
**注意**  
必须配置其他参数（例如 `Region`），但这些参数通常默认为您的 CLI 配置值。

   示例命令可能如下所示：

   ```
   aws appsync create-data-source --api-id abcdefghijklmnopqrstuvwxyz --name myFunction --type AWS_LAMBDA --lambda-config lambdaFunctionArn=arn:aws:lambda:us-west-2:102847592837:function:appsync-lambda-example
   ```

   将在 CLI 中返回输出。示例如下：

   ```
   {
       "dataSource": {
           "dataSourceArn": "arn:aws:appsync:us-west-2:102847592837:apis/abcdefghijklmnopqrstuvwxyz/datasources/myFunction",
           "type": "AWS_LAMBDA",
           "name": "myFunction",
           "lambdaConfig": {
               "lambdaFunctionArn": "arn:aws:lambda:us-west-2:102847592837:function:appsync-lambda-example"
           }
       }
   }
   ```

1. 要修改数据来源的属性，请运行 [https://awscli.amazonaws.com/v2/documentation/api/latest/reference/appsync/update-data-source.html](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/appsync/update-data-source.html) 命令。

   除了 `api-id` 参数以外，`create-data-source` 命令中使用的参数将由 `update-data-source` 命令中的新值覆盖。

------

### 激活直接 Lambda 解析器
<a name="direct-lambda-enable-templates"></a>

创建 Lambda 数据源并设置相应的 IAM 角色 AWS AppSync 以允许调用该函数后，您可以将其链接到解析器或管道函数。

------
#### [ Console ]

1. 登录 AWS 管理控制台 并打开[AppSync控制台](https://console.aws.amazon.com/appsync/)。

   1. 在**APIs 控制面板**中，选择你的 GraphQL API。

   1. 在**侧边栏**中，选择**架构**。

1. 在**解析器**窗口中，选择一个字段或操作，然后选择**附加**按钮。

1. 在**创建新解析器**页面中，从下拉列表中选择 Lambda 函数。

1. 要使用直接 Lambda 解析器，请确认在**配置映射模板**部分中禁用了请求和响应映射模板。

1. 选择**保存解析器**按钮。

------
#### [ CLI ]
+ 运行 [https://awscli.amazonaws.com/v2/documentation/api/latest/reference/appsync/create-resolver.html](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/appsync/create-resolver.html) 命令以创建解析器。

  您需要为该特定命令键入 6 个参数：

  1. 您的 API 的 `api-id`。

  1. 架构中的类型的 `type-name`。

  1. 架构中的字段的 `field-name`。

  1. `data-source-name` 或您的 Lambda 函数名称。

  1. `request-mapping-template`，这是请求的正文。在控制台示例中，已将其禁用：

     ```
     " "
     ```

  1. `response-mapping-template`，这是响应的正文。在控制台示例中，也已将其禁用：

     ```
     " "
     ```

  示例命令可能如下所示：

  ```
  aws appsync create-resolver --api-id abcdefghijklmnopqrstuvwxyz --type-name Subscription --field-name onCreateTodo --data-source-name LambdaTest --request-mapping-template " " --response-mapping-template " "
  ```

  将在 CLI 中返回输出。示例如下：

  ```
  {
      "resolver": {
          "resolverArn": "arn:aws:appsync:us-west-2:102847592837:apis/abcdefghijklmnopqrstuvwxyz/types/Subscription/resolvers/onCreateTodo",
          "typeName": "Subscription",
          "kind": "UNIT",
          "fieldName": "onCreateTodo",
          "dataSourceName": "LambdaTest"
      }
  }
  ```

------

在您禁用映射模板时，将在 AWS AppSync中出现一些其他行为：
+ 禁用映射模板 AWS AppSync 即表示您接受 Di [rect Lambda](resolver-mapping-template-reference-lambda.md#direct-lambda-resolvers) 解析器参考中指定的默认数据转换。
+ 通过禁用请求映射模板，您的 Lambda 数据来源将接收包含整个[上下文](resolver-context-reference.md)对象的负载。
+ 通过禁用响应映射模板，将转换您的 Lambda 调用结果，具体取决于请求映射模板版本或是否还禁用了请求映射模板。

# 在 AWS AppSync (VTL) 中测试和调试解析器
<a name="test-debug-resolvers"></a>

**注意**  
我们现在主要支持 APPSYNC\$1JS 运行时系统及其文档。请考虑使用 APPSYNC\$1JS 运行时系统和[此处](https://docs.aws.amazon.com/appsync/latest/devguide/configuring-resolvers-js.html)的指南。

AWS AppSync 针对数据源对 GraphQL 字段执行解析器。正如[解析器映射模板概述](resolver-mapping-template-reference-overview.md#aws-appsync-resolver-mapping-template-reference-overview)中所述，解析器使用模板语言与数据来源进行通信。这样，您就可以在与数据来源通信之前和之后自定义行为并应用逻辑和条件。有关编写解析器的入门教程风格的编程指南，请参阅[解析器映射模板编程指南](resolver-mapping-template-reference-programming-guide.md#aws-appsync-resolver-mapping-template-reference-programming-guide)。

为了帮助开发者编写、测试和调试这些解析器， AWS AppSync 控制台还提供了一些工具，用于创建 GraphQL 请求和响应，并将模拟数据传递给各个字段解析器。此外，您还可以在 AWS AppSync 控制台中执行查询、变更和订阅，并查看来自 Amazon CloudWatch 的整个请求的详细日志流。其中包括从数据来源获得的结果。

## 使用模拟数据进行测试
<a name="testing-with-mock-data"></a>

在调用 GraphQL 解析器时，它包含一个 `context` 对象，其中包含有关请求的信息。其中包括来自客户端的参数、身份信息以及 GraphQL 父字段的数据。它还包含来自数据来源的结果，可以在响应模板中使用这些结果。有关此结构的更多信息以及在编程时可用的帮助程序实用程序，请参阅[解析器映射模板上下文参考](resolver-context-reference.md#aws-appsync-resolver-mapping-template-context-reference)。

在编写或编辑解析器时，您可以将*模拟* 或*测试上下文* 对象传递到控制台编辑器中。这使您能够查看请求和响应模板如何评估，而不必针对数据来源实际运行。例如，您可以传递测试 `firstname: Shaggy` 参数，观察在您的模板代码中使用 `$ctx.args.firstname` 时如何评估。您还可以测试任何实用程序帮助程序（例如 `$util.autoId()` 或 `util.time.nowISO8601()`）的评估。

### 测试解析器
<a name="test-a-resolver"></a>

此示例将使用 AWS AppSync 控制台测试解析器。

1. 登录 AWS 管理控制台 并打开[AppSync控制台](https://console.aws.amazon.com/appsync/)。

   1. 在**APIs 控制面板**中，选择你的 GraphQL API。

   1. 在**侧边栏**中，选择**架构**。

1. 如果您尚未在类型下面的字段旁边选择**附加**以添加解析器，请执行该操作。

   有关如何构建完整解析器的更多信息，请参阅[配置解析器](https://docs.aws.amazon.com/appsync/latest/devguide/configuring-resolvers.html)。

   否则，选择已位于字段中的解析器。

1. 在**编辑解析器**页面顶部，选择**选择测试上下文**，然后选择**创建新上下文**。

1. 选择一个示例上下文对象，或者在下面的**执行上下文**窗口中手动填充 JSON。

1. 输入**测试上下文名称**。

1. 选择**保存**按钮。

1. 在**编辑解析器**页面顶部，选择**运行测试**。

举一个更实际的例子，假设您具有一个存储 GraphQL 类型 `Dog` 的应用程序，该应用程序自动为对象生成 ID 并将其存储在 Amazon DynamoDB 中。您还希望通过 GraphQL 变更参数写入一些值，并仅允许特定用户查看响应。下面显示了架构的外观：

```
type Dog {
  breed: String
  color: String
}

type Mutation {
  addDog(firstname: String, age: Int): Dog
}
```

在您为 `addDog` 变更添加解析器时，您可以填充上下文对象，如以下示例中所示。以下代码拥有 `name` 和 `age` 客户端的参数，以及 `identity` 对象中填充的 `username`：

```
{
    "arguments" : {
        "firstname": "Shaggy",
        "age": 4
    },
    "source" : {},
    "result" : {
        "breed" : "Miniature Schnauzer",
        "color" : "black_grey"
    },
    "identity": {
        "sub" : "uuid",
        "issuer" : " https://cognito-idp.{region}.amazonaws.com/{userPoolId}",
        "username" : "Nadia",
        "claims" : { },
        "sourceIp" :[  "x.x.x.x" ],
        "defaultAuthStrategy" : "ALLOW"
    }
}
```

您可以使用以下请求和响应映射模板测试此内容：

 **请求模板** 

```
{
    "version" : "2017-02-28",
    "operation" : "PutItem",
    "key" : {
        "id" : { "S" : "$util.autoId()" }
    },
    "attributeValues" : $util.dynamodb.toMapValuesJson($ctx.args)
}
```

 **响应模板** 

```
#if ($context.identity.username == "Nadia")
  $util.toJson($ctx.result)
#else
  $util.unauthorized()
#end
```

经过评估的模板拥有您的测试上下文对象的数据，以及 `$util.autoId()` 生成的值。此外，如果您要将 `username` 更改为除 `Nadia` 之外的值，将不会返回结果，因为授权检查将失败。有关精细访问控制的更多信息，请参阅[授权使用案例](security-authorization-use-cases.md#aws-appsync-security-authorization-use-cases)。

### 使用 AWS AppSync's 测试映射模板 APIs
<a name="testing-with-appsync-api"></a>

您可以通过 `EvaluateMappingTemplate` API 命令使用模拟数据远程测试您的映射模板。要开始使用该命令，请确保您已将 `appsync:evaluateMappingTemplate` 权限添加到您的策略中。例如：

------
#### [ JSON ]

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "appsync:evaluateMappingTemplate",
            "Resource": "arn:aws:appsync:us-east-1:111122223333:*"
        }
    ]
}
```

------

您可以使用[AWS CLI](https://aws.amazon.com/cli/)或来利用该命令[AWS SDKs](https://aws.amazon.com/tools/)。例如，以上一节中的`Dog`架构及其 request/response 映射模板为例。通过在本地工作站上使用 CLI，将请求模板保存到名为 `request.vtl` 的文件中，然后将 `context` 对象保存到名为 `context.json` 的文件中。从 Shell 中，运行以下命令：

```
aws appsync evaluate-mapping-template --template file://request.vtl --context file://context.json
```

该命令返回以下响应：

```
{
  "evaluationResult": "{\n    \"version\" : \"2017-02-28\",\n    \"operation\" : \"PutItem\",\n    \"key\" : {\n        \"id\" : { \"S\" : \"afcb4c85-49f8-40de-8f2b-248949176456\" }\n    },\n    \"attributeValues\" : {\"firstname\":{\"S\":\"Shaggy\"},\"age\":{\"N\":4}}\n}\n"
}
```

`evaluationResult` 包含使用提供的 `context` 测试您提供的模板的结果。您也可以使用测试您的模板 AWS SDKs。以下是使用适用于 JavaScript V2 的 AWS SDK 的示例：

```
const AWS = require('aws-sdk')
const client = new AWS.AppSync({ region: 'us-east-2' })

const template = fs.readFileSync('./request.vtl', 'utf8')
const context = fs.readFileSync('./context.json', 'utf8')

client
  .evaluateMappingTemplate({ template, context })
  .promise()
  .then((data) => console.log(data))
```

通过使用 SDK，您可以轻松合并常用的测试套件中的测试以验证模板行为。我们建议使用 [Jest 测试框架](https://jestjs.io/)创建测试，但任何测试套件都有效。以下代码片段显示假设的验证运行。请注意，我们希望评估响应是有效的 JSON ，因此，我们使用 `JSON.parse` 从字符串响应中检索 JSON：

```
const AWS = require('aws-sdk')
const fs = require('fs')
const client = new AWS.AppSync({ region: 'us-east-2' })

test('request correctly calls DynamoDB', async () => {
  const template = fs.readFileSync('./request.vtl', 'utf8')
  const context = fs.readFileSync('./context.json', 'utf8')
  const contextJSON = JSON.parse(context)
  
  const response = await client.evaluateMappingTemplate({ template, context }).promise()
  const result = JSON.parse(response.evaluationResult)
  
  expect(result.key.id.S).toBeDefined()
  expect(result.attributeValues.firstname.S).toEqual(contextJSON.arguments.firstname)
})
```

 这会产生以下结果：

```
Ran all test suites.
> jest

PASS ./index.test.js
✓ request correctly calls DynamoDB (543 ms)

Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 1.511 s, estimated 2 s
```

## 调试实时查询
<a name="debugging-a-live-query"></a>

除了调试生产应用程序的 end-to-end测试和日志记录之外，别无选择。 AWS AppSync 允许您使用 Amazon 记录错误和完整的请求详情 CloudWatch。此外，您可以使用 AWS AppSync 控制台测试 GraphQL 查询、突变和订阅，并将每个请求的实时日志数据流回查询编辑器进行实时调试。对于订阅而言，日志显示连接时间信息。

要执行此操作，您需要提前启用 Amazon CloudWatch 日志，如[监控和日志](monitoring.md#aws-appsync-monitoring)中所述。接下来，在 AWS AppSync 控制台中选择 “**查询**” 选项卡，然后输入有效的 GraphQL 查询。在右下部分中，单击并拖动**日志**窗口以打开“日志”视图。在页面顶部，选择“播放”箭头图标运行您的 GraphQL 查询。片刻之后，此操作的完整请求和响应日志将流式传输到控制台的这一部分，之后您可以在控制台中进行查看。

# 在 AWS AppSync (VTL) 中配置和使用管道解析器
<a name="pipeline-resolvers"></a>

**注意**  
我们现在主要支持 APPSYNC\$1JS 运行时系统及其文档。请考虑使用 APPSYNC\$1JS 运行时系统和[此处](https://docs.aws.amazon.com/appsync/latest/devguide/configuring-resolvers-js.html)的指南。

AWS AppSync 在 GraphQL 字段上执行解析器。在某些情况下，应用程序需要执行多个操作以解析单个 GraphQL 字段。通过使用管道解析器，开发人员现在可以编写称为“函数”的操作并按顺序执行它们。例如，管道解析器对于需要在获取字段数据之前执行授权检查的应用程序非常有用。

管道解析器由**之前** 映射模板、**之后** 映射模板和一组函数组成。每个函数具有对数据来源执行的**请求**映射模板和**响应**映射模板。由于管道解析器将执行委托给函数列表，因此它不会链接到任何数据来源。单位解析器和函数是对数据来源执行操作的基元。有关更多信息，请参阅[解析器映射模板概述](resolver-mapping-template-reference-overview.md#aws-appsync-resolver-mapping-template-reference-overview)。

## 步骤 1：创建管线解析器
<a name="create-a-pipeline-resolver"></a>

在 AWS AppSync 控制台中，转到 “**架构**” 页面。

保存以下架构：

```
schema {
    query: Query
    mutation: Mutation
}

type Mutation {
    signUp(input: Signup): User
}

type Query {
    getUser(id: ID!): User
}

input Signup {
    username: String!
    email: String!
}

type User {
    id: ID!
    username: String
    email: AWSEmail
}
```

我们将一个管道解析器连接到 **Mutation** 类型的 **signUp** 字段。在右侧的 **Mutation** 类型中，选择 `signUp` 变更字段旁边的**附加**。在“创建解析器”页面上，单击**操作**，然后单击**更新运行时**。依次选择`Pipeline Resolver`、`VTL` 和**更新**。该页面现在应显示三个部分：**之前映射模板**文本区域、**函数**部分和**之后映射模板**文本区域。

我们的管道解析器通过先验证电子邮件地址输入，然后将用户保存在系统中来注册用户。我们将电子邮件验证封装在 **validateEmail** 函数中，并将用户保存在 **saveUser** 函数中。首先执行 **validateEmail** 函数，如果电子邮件有效，则执行 **saveUser** 函数。

执行流将如下所示：

1. Mutation.signUp 解析器请求映射模板

1. validateEmail 函数

1. saveUser 函数

1. Mutation.signUp 解析器响应映射模板

由于我们可能在 API 上的其他解析器中重复使用 **validateEmail** 函数，因此，我们希望避免访问 `$ctx.args`，因为它们将从一个 GraphQL 字段更改为另一个字段。相反，我们可以使用 `$ctx.stash` 存储 `signUp(input: Signup)` 输入字段参数中的电子邮件属性。

**之前**映射模板：

```
## store email input field into a generic email key
$util.qr($ctx.stash.put("email", $ctx.args.input.email))
{}
```

控制台提供一个我们将使用的默认传递**之后**映射模板：

```
$util.toJson($ctx.result)
```

选择**创建**或**保存**以更新解析器。

## 步骤 2：创建函数
<a name="create-a-function"></a>

从管道解析器页面的**函数**部分中，单击**添加函数**，然后单击**创建新函数**。也可以在不通过解析器页面的情况下创建函数；要执行此操作，请在 AWS AppSync控制台中转到 “**函数**” 页面。选择**创建函数**按钮。让我们创建一个函数来检查电子邮件是否有效并来自特定域。如果电子邮件无效，该函数会引发一个错误。否则，它将转发接收到的任何输入。

在新函数页面上，选择**操作**，然后选择**更新运行时**。选择 `VTL`，然后选择**更新**。确保您已创建一个 **NONE** 类型的数据来源。在**数据来源名称**列表中选择该数据来源。对于**函数名称**，请输入 `validateEmail`。在**函数代码**区域中，使用以下代码片段覆盖所有内容：

```
#set($valid = $util.matches("^[a-zA-Z0-9_.+-]+@(?:(?:[a-zA-Z0-9-]+\.)?[a-zA-Z]+\.)?(myvaliddomain)\.com", $ctx.stash.email))
#if (!$valid)
    $util.error("$ctx.stash.email is not a valid email.")
#end
{
    "payload": { "email": $util.toJson(${ctx.stash.email}) }
}
```

将其粘贴到响应映射模板中：

```
$util.toJson($ctx.result)
```

检查您的更改，然后选择**创建**。我们刚刚创建了 **validateEmail** 函数。重复这些步骤以创建具有以下请求和响应映射模板的 **saveUser** 函数（为了简单起见，我们使用 **NONE** 数据来源，并假设在函数执行后已将用户保存在系统中）：

请求映射模板：

```
## $ctx.prev.result contains the signup input values. We could have also
## used $ctx.args.input.
{
    "payload": $util.toJson($ctx.prev.result)
}
```

响应映射模板：

```
## an id is required so let's add a unique random identifier to the output
$util.qr($ctx.result.put("id", $util.autoId()))
$util.toJson($ctx.result)
```

我们刚刚创建了 **saveUser** 函数。

## 步骤 3：将函数添加到管线解析器
<a name="adding-a-function-to-a-pipeline-resolver"></a>

我们的函数应该已自动添加到刚刚创建的管道解析器中。如果情况并非如此，或者您通过**函数**页面创建了函数，您可以单击解析器页面上的**添加函数**以附加这些函数。将 **validateEmail** 和 **saveUser** 函数添加到解析器中。**validateEmail** 函数应放在 **saveUser** 函数之前。在添加更多函数时，您可以使用**上移**和**下移**选项重新排列函数的执行顺序。检查您的更改，然后选择**保存**。

## 步骤 4：执行查询
<a name="executing-a-query"></a>

在 AWS AppSync 控制台中，转到 “**查询**” 页面。在资源管理器中，确保您正在使用变更。如果不是，请在下拉列表中选择`Mutation`，然后选择 `+`。输入以下查询：

```
mutation {
  signUp(input: {
    email: "nadia@myvaliddomain.com"
    username: "nadia"
  }) {
    id
    email
  }
}
```

它应返回如下内容：

```
{
  "data": {
    "signUp": {
      "id": "256b6cc2-4694-46f4-a55e-8cb14cc5d7fc",
      "email": "nadia@myvaliddomain.com"
    }
  }
}
```

我们已成功注册用户，并使用管道解析器验证了输入电子邮件。要遵循有关管道解析器的更完整的教程，您可以转到[教程：管道解析器](tutorial-pipeline-resolvers.md#aws-appsync-tutorial-pipeline-resolvers) 

# 在 AWS CDK 中使用 AWS AppSync API
<a name="using-your-api"></a>

**提示**  
在使用 CDK 之前，我们建议您查看 CDK 的[官方文档](https://docs.aws.amazon.com/cdk/v2/guide/getting_started.html)以及 [CD](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_appsync-readme.html) K 参考资料。 AWS AppSync  
我们还建议确保 [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html) 和 [NPM](https://docs.npmjs.com/) 安装在您的系统上正常工作。

在本节中，我们将创建一个简单的 CDK 应用程序，该应用程序可以在 DynamoDB 表中添加和获取项目。这是一个快速入门示例，使用了 “[设计架构](https://docs.aws.amazon.com/appsync/latest/devguide/designing-your-schema.html)”、“[附加数据源](https://docs.aws.amazon.com/appsync/latest/devguide/attaching-a-data-source.html)” 和 “[配置解析器” () JavaScript](https://docs.aws.amazon.com/appsync/latest/devguide/configuring-resolvers-js.html) 部分中的一些代码。

## 设置 CDK 项目
<a name="Setting-up-a-cdk-project"></a>

**警告**  
根据您的环境，这些步骤可能不完全准确。我们假设您的系统已安装必要的实用程序、与 AWS 服务接口的方法以及适当的配置。

第一步是安装 AWS CDK。在 CLI 中，您可以输入以下命令：

```
npm install -g aws-cdk
```

接下来，您需要创建一个项目目录，然后导航到该目录。用于创建和导航到目录的一组示例命令是：

```
mkdir example-cdk-app
cd example-cdk-app
```

接下来，您需要创建一个应用程序。我们的服务主要使用 TypeScript. 在您的项目目录中，输入以下命令：

```
cdk init app --language typescript
```

在您执行该操作时，将安装 CDK 应用程序及其初始化文件：

![\[Terminal output showing Git repository initialization and npm install completion.\]](http://docs.aws.amazon.com/zh_cn/appsync/latest/devguide/images/cdk-init-app-example.png)


您的项目结构可能如下所示：

![\[Project directory structure showing folders and files for an example CDK app.\]](http://docs.aws.amazon.com/zh_cn/appsync/latest/devguide/images/cdk-init-directories.png)


您会注意到我们具有几个重要目录：
+ `bin`：初始 bin 文件将创建应用程序。我们在本指南中不会对其进行修改。
+ `lib`：lib 目录包含您的堆栈文件。您可以将堆栈文件视为单独的执行单元。构造将位于我们的堆栈文件中。基本上，这些是部署应用程序 CloudFormation 时将启动的服务的资源。这是我们完成大部分编码的位置。
+ `node_modules`：该目录由 NPM 创建，并包含您使用 `npm` 命令安装的所有包依赖项。

我们的初始堆栈文件可能包含如下内容：

```
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
// import * as sqs from 'aws-cdk-lib/aws-sqs';

export class ExampleCdkAppStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    // The code that defines your stack goes here

    // example resource
    // const queue = new sqs.Queue(this, 'ExampleCdkAppQueue', {
    //   visibilityTimeout: cdk.Duration.seconds(300)
    // });
  }
}
```

这是在我们的应用程序中创建堆栈的样板代码。该示例中的大部分代码都位于该类的范围内。

要验证您的堆栈文件是否位于应用程序的目录中，请在终端中运行以下命令：

```
cdk ls
```

应该会显示您的堆栈列表。如果没有显示，您可能需要再次执行这些步骤，或者查看官方文档以获得帮助。

如果要在部署之前构建代码更改，您始终可以在终端中运行以下命令：

```
npm run build
```

要在部署之前查看更改，请运行以下命令：

```
cdk diff
```

在将代码添加到堆栈文件之前，我们将执行引导。通过进行引导，我们可以在部署应用程序之前为 CDK 预置资源。可以在[此处](https://docs.aws.amazon.com/cdk/v2/guide/bootstrapping.html)找到有关该过程的更多信息。要创建引导，请运行以下命令：

```
cdk bootstrap aws://ACCOUNT-NUMBER/REGION
```

**提示**  
该步骤要求在您的账户中具有多个 IAM 权限。如果您没有这些权限，将拒绝您的引导。如果发生这种情况，您可能必须删除引导导致的不完整资源，例如，引导生成的 S3 存储桶。

引导将启动多个资源。最终消息将如下所示：

![\[Terminal output showing successful bootstrapping of an AWS environment.\]](http://docs.aws.amazon.com/zh_cn/appsync/latest/devguide/images/cdk-init-bootstrap-final.png)


该操作对每个账户的每个区域执行一次，因此，您无需经常这样做。引导程序的主要资源是 CloudFormation 堆栈和 Amazon S3 存储桶。

Amazon S3 存储桶用于存储文件和 IAM 角色，这些角色授予执行部署所需的权限。所需的资源是在一个名为引导 CloudFormation 堆栈的堆栈中定义的，该堆栈通常被命名为`CDKToolkit`。与任何 CloudFormation 堆栈一样，它在部署后会显示在 CloudFormation 控制台中：

![\[CDKToolkit stack with CREATE_COMPLETE status in CloudFormation console.\]](http://docs.aws.amazon.com/zh_cn/appsync/latest/devguide/images/cdk-init-bootstrap-cfn-console.png)


存储桶也是如此：

![\[S3 bucket details showing name, region, access settings, and creation date.\]](http://docs.aws.amazon.com/zh_cn/appsync/latest/devguide/images/cdk-init-bootstrap-bucket-console.png)


要在堆栈文件中导入所需的服务，我们可以使用以下命令：

```
npm install aws-cdk-lib # V2 command
```

**提示**  
如果您在使用 V2 时遇到问题，您可以使用 V1 命令安装各个库：  

```
npm install @aws-cdk/aws-appsync @aws-cdk/aws-dynamodb
```
我们不建议这样做，因为 V1 已被弃用。

## 实施 CDK 项目 - 架构
<a name="implementing-a-cdk-project-schema"></a>

我们现在可以开始实施代码了。首先，我们必须创建架构。您只需在应用程序中创建一个 `.graphql` 文件即可：

```
mkdir schema
touch schema.graphql
```

在我们的示例中，我们包含一个名为 `schema` 的顶级目录，其中包含 `schema.graphql`：

![\[File structure showing a schema folder containing schema.graphql file.\]](http://docs.aws.amazon.com/zh_cn/appsync/latest/devguide/images/cdk-code-schema-directory.png)


在我们的架构中，让我们包含一个简单的示例：

```
input CreatePostInput {
    title: String
    content: String
}

type Post {
    id: ID!
    title: String
    content: String
}

type Mutation {
    createPost(input: CreatePostInput!): Post
}

type Query {
    getPost: [Post]
}
```

回到我们的堆栈文件，我们需要确保定义了以下 import 指令：

```
import * as cdk from 'aws-cdk-lib';
import * as appsync from 'aws-cdk-lib/aws-appsync';
import * as dynamodb from 'aws-cdk-lib/aws-dynamodb';
import { Construct } from 'constructs';
```

在类中，我们将添加代码以创建 GraphQL API 并将其连接到 `schema.graphql` 文件：

```
export class ExampleCdkAppStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);
    
    // makes a GraphQL API
    const api = new appsync.GraphqlApi(this, 'post-apis', {
      name: 'api-to-process-posts',
      schema: appsync.SchemaFile.fromAsset('schema/schema.graphql'),
    });
  }
}
```

我们还会添加一些代码以输出 GraphQL URL、API 密钥和区域：

```
export class ExampleCdkAppStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);
    
    // Makes a GraphQL API construct
    const api = new appsync.GraphqlApi(this, 'post-apis', {
      name: 'api-to-process-posts',
      schema: appsync.SchemaFile.fromAsset('schema/schema.graphql'),
    });

    // Prints out URL
    new cdk.CfnOutput(this, "GraphQLAPIURL", {
      value: api.graphqlUrl
    });

    // Prints out the AppSync GraphQL API key to the terminal
    new cdk.CfnOutput(this, "GraphQLAPIKey", {
      value: api.apiKey || ''
    });

    // Prints out the stack region to the terminal
    new cdk.CfnOutput(this, "Stack Region", {
      value: this.region
    });
  }
}
```

此时，再次部署我们的应用程序：

```
cdk deploy
```

结果如下所示：

![\[Deployment output showing ExampleCdkAppStack details, including GraphQL API URL and stack region.\]](http://docs.aws.amazon.com/zh_cn/appsync/latest/devguide/images/cdk-code-deploy-schema.png)


看来我们的示例成功了，但让我们检查一下 AWS AppSync 控制台以确认：

![\[GraphQL interface showing successful API request with response data displayed.\]](http://docs.aws.amazon.com/zh_cn/appsync/latest/devguide/images/cdk-code-deploy-schema-result-1.png)


看来已创建了我们的 API。现在，我们将检查附加到 API 的架构：

![\[GraphQL schema defining CreatePostInput, Post type, Mutation, and Query operations.\]](http://docs.aws.amazon.com/zh_cn/appsync/latest/devguide/images/cdk-code-deploy-schema-result-2.png)


这似乎与我们的架构代码匹配，因此，它是成功的。从元数据角度确认这一点的另一种方法是查看 CloudFormation 堆栈：

![\[CloudFormation stack showing ExampleCdkAppStack update complete and CDKToolkit creation complete.\]](http://docs.aws.amazon.com/zh_cn/appsync/latest/devguide/images/cdk-code-deploy-schema-result-3.png)


当我们部署 CDK 应用程序时，它会启动引导程序等资源。 CloudFormation 我们的应用程序中的每个堆栈都与一个堆 CloudFormation 栈以 1:1 的比分映射。如果您回到堆栈代码，就会发现堆栈名称是从类名称 `ExampleCdkAppStack` 中获取的。您可以看到它创建的资源，这些资源也符合 GraphQL API 构造中的命名约定：

![\[Expanded view of post-apis resource showing Schema, DefaultApiKey, and CDKMetadata.\]](http://docs.aws.amazon.com/zh_cn/appsync/latest/devguide/images/cdk-code-deploy-schema-result-4.png)


## 实施 CDK 项目 - 数据来源
<a name="implementing-a-cdk-project-data-source"></a>

接下来，我们需要添加数据来源。我们的示例使用 DynamoDB 表。在堆栈类中，我们添加一些代码以创建新的表：

```
export class ExampleCdkAppStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    // Makes a GraphQL API construct
    const api = new appsync.GraphqlApi(this, 'post-apis', {
      name: 'api-to-process-posts',
      schema: appsync.SchemaFile.fromAsset('schema/schema.graphql'),
    });

    //creates a DDB table
    const add_ddb_table = new dynamodb.Table(this, 'posts-table', {
      partitionKey: {
        name: 'id',
        type: dynamodb.AttributeType.STRING,
      },
    });

    // Prints out URL
    new cdk.CfnOutput(this, "GraphQLAPIURL", {
      value: api.graphqlUrl
    });

    // Prints out the AppSync GraphQL API key to the terminal
    new cdk.CfnOutput(this, "GraphQLAPIKey", {
      value: api.apiKey || ''
    });

    // Prints out the stack region to the terminal
    new cdk.CfnOutput(this, "Stack Region", {
      value: this.region
    });
  }
}
```

此时，让我们再次部署：

```
cdk deploy
```

我们应该检查 DynamoDB 控制台是否有新的表：

![\[DynamoDB console showing ExampleCdkAppStack-poststable as Active with Provisioned capacity.\]](http://docs.aws.amazon.com/zh_cn/appsync/latest/devguide/images/cdk-code-deploy-ddb-result-1.png)


我们的堆栈名称是正确的，并且表名称与代码匹配。如果我们再次检查 CloudFormation 堆栈，我们现在将看到新表：

![\[Expanded view of a logical ID in CloudFormation showing post-apis, posts-table, and CDKMetadata.\]](http://docs.aws.amazon.com/zh_cn/appsync/latest/devguide/images/cdk-code-deploy-ddb-result-2.png)


## 实施 CDK 项目 - 解析器
<a name="implementing-a-cdk-project-resolver"></a>

该示例将使用两个解析器：一个用于查询表，另一个用于在表中添加数据。由于我们使用管道解析器，因此，我们需要声明两个管道解析器，在每个解析器中具有一个函数。在查询中，我们添加以下代码：

```
export class ExampleCdkAppStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    // Makes a GraphQL API construct
    const api = new appsync.GraphqlApi(this, 'post-apis', {
      name: 'api-to-process-posts',
      schema: appsync.SchemaFile.fromAsset('schema/schema.graphql'),
    });

    //creates a DDB table
    const add_ddb_table = new dynamodb.Table(this, 'posts-table', {
      partitionKey: {
        name: 'id',
        type: dynamodb.AttributeType.STRING,
      },
    });

    // Creates a function for query
    const add_func = new appsync.AppsyncFunction(this, 'func-get-post', {
      name: 'get_posts_func_1',
      api,
      dataSource: api.addDynamoDbDataSource('table-for-posts', add_ddb_table),
      code: appsync.Code.fromInline(`
          export function request(ctx) {
          return { operation: 'Scan' };
          }

          export function response(ctx) {
          return ctx.result.items;
          }
  `),
      runtime: appsync.FunctionRuntime.JS_1_0_0,
    });

    // Creates a function for mutation
    const add_func_2 = new appsync.AppsyncFunction(this, 'func-add-post', {
      name: 'add_posts_func_1',
      api,
      dataSource: api.addDynamoDbDataSource('table-for-posts-2', add_ddb_table),
      code: appsync.Code.fromInline(`
          export function request(ctx) {
            return {
            operation: 'PutItem',
            key: util.dynamodb.toMapValues({id: util.autoId()}),
            attributeValues: util.dynamodb.toMapValues(ctx.args.input),
            };
          }

          export function response(ctx) {
            return ctx.result;
          }
      `),
      runtime: appsync.FunctionRuntime.JS_1_0_0,
    });

    // Adds a pipeline resolver with the get function
    new appsync.Resolver(this, 'pipeline-resolver-get-posts', {
      api,
      typeName: 'Query',
      fieldName: 'getPost',
      code: appsync.Code.fromInline(`
          export function request(ctx) {
          return {};
          }

          export function response(ctx) {
          return ctx.prev.result;
          }
  `),
      runtime: appsync.FunctionRuntime.JS_1_0_0,
      pipelineConfig: [add_func],
    });

    // Adds a pipeline resolver with the create function
    new appsync.Resolver(this, 'pipeline-resolver-create-posts', {
      api,
      typeName: 'Mutation',
      fieldName: 'createPost',
      code: appsync.Code.fromInline(`
          export function request(ctx) {
          return {};
          }

          export function response(ctx) {
          return ctx.prev.result;
          }
  `),
      runtime: appsync.FunctionRuntime.JS_1_0_0,
      pipelineConfig: [add_func_2],
    });

    // Prints out URL
    new cdk.CfnOutput(this, "GraphQLAPIURL", {
      value: api.graphqlUrl
    });

    // Prints out the AppSync GraphQL API key to the terminal
    new cdk.CfnOutput(this, "GraphQLAPIKey", {
      value: api.apiKey || ''
    });

    // Prints out the stack region to the terminal
    new cdk.CfnOutput(this, "Stack Region", {
      value: this.region
    });
  }
}
```

在该代码片段中，我们添加一个名为 `pipeline-resolver-create-posts` 的管道解析器，并将一个名为 `func-add-post` 的函数附加到该解析器。这是将 `Posts` 添加到表中的代码。另一个管道解析器命名为 `pipeline-resolver-get-posts`，并具有一个名为 `func-get-post` 的函数，该函数检索添加到表中的 `Posts`。

我们将部署它以将其添加到 AWS AppSync 服务中：

```
cdk deploy
```

让我们检查一下 AWS AppSync 控制台，看看它们是否已连接到我们的 GraphQL API：

![\[GraphQL API schema showing mutation and query fields with Pipeline resolvers.\]](http://docs.aws.amazon.com/zh_cn/appsync/latest/devguide/images/cdk-code-deploy-resolver-result-1.png)


看来是正确的。在代码中，这两个解析器都附加到我们创建的 GraphQL API（由解析器和函数中存在的 `api` 属性值表示）。在 GraphQL API 中，我们将解析器附加到的字段也是在属性中指定的（由每个解析器中的 `typename` 和 `fieldname` 属性定义）。

让我们看看解析器内容是否正确，从 `pipeline-resolver-get-posts` 开始：

![\[Code snippet showing request and response functions in a resolver, with an arrow pointing to them.\]](http://docs.aws.amazon.com/zh_cn/appsync/latest/devguide/images/cdk-code-deploy-resolver-result-2.png)


之前和之后处理程序与 `code` 属性值匹配。我们还可以看到一个名为 `add_posts_func_1` 的函数，它与我们在解析器中附加的函数的名称匹配。

我们看一下该函数的代码内容：

![\[Function code showing request and response methods for a PutItem operation.\]](http://docs.aws.amazon.com/zh_cn/appsync/latest/devguide/images/cdk-code-deploy-resolver-result-3.png)


这与 `add_posts_func_1` 函数的 `code` 属性匹配。已成功上传我们的查询，因此，让我们检查一下该查询：

![\[Resolver code with request and response functions, and a get_posts_func_1 function listed below.\]](http://docs.aws.amazon.com/zh_cn/appsync/latest/devguide/images/cdk-code-deploy-resolver-result-4.png)


这些也与代码匹配。如果我们看一下 `get_posts_func_1`：

![\[Code snippet showing two exported functions: request returning 'Scan' operation and response returning items.\]](http://docs.aws.amazon.com/zh_cn/appsync/latest/devguide/images/cdk-code-deploy-resolver-result-5.png)


一切似乎准备就绪。要从元数据角度确认这一点，我们可以再次在 CloudFormation 中检查堆栈：

![\[List of logical IDs for AWS resources including API, table, functions, and pipelines.\]](http://docs.aws.amazon.com/zh_cn/appsync/latest/devguide/images/cdk-code-deploy-resolver-result-6.png)


现在，我们需要执行一些请求以测试该代码。

## 实施 CDK 项目 - 请求
<a name="implementing-a-cdk-project-requests"></a>

为了在 AWS AppSync 控制台中测试我们的应用程序，我们做了一个查询和一个变更：

![\[GraphQL code snippet showing a query to get post details and a mutation to create a post.\]](http://docs.aws.amazon.com/zh_cn/appsync/latest/devguide/images/cdk-code-request-1.png)


`MyMutation` 包含具有 `createPost` 和 `1970-01-01T12:30:00.000Z` 参数的 `first post` 操作。它返回我们传入的 `date` 和 `title` 以及自动生成的 `id` 值。运行该变更将产生以下结果：

```
{
  "data": {
    "createPost": {
      "date": "1970-01-01T12:30:00.000Z",
      "id": "4dc1c2dd-0aa3-4055-9eca-7c140062ada2",
      "title": "first post"
    }
  }
}
```

如果快速检查 DynamoDB 表，我们可以在扫描时在该表中看到我们的条目：

![\[DynamoDB table entry showing id, date, and title fields for a single item.\]](http://docs.aws.amazon.com/zh_cn/appsync/latest/devguide/images/cdk-code-request-2.png)


回到 AWS AppSync 控制台，如果我们运行查询来检索它`Post`，我们会得到以下结果：

```
{
  "data": {
    "getPost": [
      {
        "id": "9f62c4dd-49d5-48d5-b835-143284c72fe0",
        "date": "1970-01-01T12:30:00.000Z",
        "title": "first post"
      }
    ]
  }
}
```

# 在中使用实时数据应用程序的订阅 AWS AppSync
<a name="aws-appsync-real-time-data"></a>

**重要**  
从 2025 年 3 月 13 日起， WebSockets 您可以使用 AWS AppSync 事件构建实时 PubSub API。有关更多信息，请参阅[事件开发者指南 WebSocket中的通过发布AWS](https://docs.aws.amazon.com/appsync/latest/eventapi/publish-websocket.html)*AppSync 事件*。

AWS AppSync允许您利用订阅来实现实时应用程序更新、推送通知等。当客户端调用 GraphQL 订阅操作时，由自动建立和维护安全 WebSocket 连接。 AWS AppSync然后，应用程序可以将数据源中的数据实时分发给订阅者，同时 AWS AppSync 持续管理应用程序的连接和扩展需求。以下各节将向您展示订阅 AWS AppSync的工作原理。

## GraphQL 架构订阅指令
<a name="graphql-schema-subscription-directives"></a>

中的订阅 AWS AppSync 是作为对突变的响应而调用的。这意味着您可以通过在突变上指定 GraphQL 架构指令来 AWS AppSync 实时创建任何数据源。

 AWS Amplify 客户端库会自动处理订阅连接管理。这些库使用 pure WebSockets 作为客户端和服务之间的网络协议。

**注意**  
要控制订阅连接时的授权，您可以使用 AWS Identity and Access Management (IAM)、、Amazon Cognito 身份池或 Amazon Cognito 用户池进行字段级授权。 AWS Lambda要对订阅进行精细的访问控制，您可以将解析器附加到订阅字段，并使用调用者和 AWS AppSync 数据源的身份执行逻辑。有关更多信息，请参阅 [配置授权和身份验证以保护您的 GraphQL APIs](security-authz.md)。

订阅由变更触发，并将变更选择集发送给订阅者。

以下示例展示了如何使用 GraphQL 订阅。它没有指定数据源，因为数据源可能是 Lambda、Amazon DynamoDB 或亚马逊服务。 OpenSearch 

要开始使用订阅，您必须将订阅入口点添加到您的架构中，如下所示：

```
schema {
    query: Query
    mutation: Mutation
    subscription: Subscription
}
```

假设有一个博客站点，您希望订阅新博文和现有博客的变更。为此，请在架构中添加以下 `Subscription` 定义：

```
type Subscription {
    addedPost: Post
    updatedPost: Post
    deletedPost: Post
}
```

进一步假设有以下变更：

```
type Mutation {
    addPost(id: ID! author: String! title: String content: String url: String): Post!
    updatePost(id: ID! author: String! title: String content: String url: String ups: Int! downs: Int! expectedVersion: Int!): Post!
    deletePost(id: ID!): Post!
}
```

对于希望收到通知的每个订阅，您可以添加 `@aws_subscribe(mutations: ["mutation_field_1", "mutation_field_2"])` 指令，使这些字段成为实时字段，如下所示：

```
type Subscription {
    addedPost: Post
    @aws_subscribe(mutations: ["addPost"])
    updatedPost: Post
    @aws_subscribe(mutations: ["updatePost"])
    deletedPost: Post
    @aws_subscribe(mutations: ["deletePost"])
}
```

由于 `@aws_subscribe(mutations: ["",..,""])` 使用变更输入数组，因此，您可以指定多个启动订阅的变更。如果您从客户端订阅，您的 GraphQL 查询可能是下面的样子：

```
subscription NewPostSub {
    addedPost {
        __typename
        version
        title
        content
        author
        url
    }
}
```

客户端连接和工具需要使用该订阅查询。

对于纯 WebSockets 客户端，选择集筛选是针对每个客户机进行的，因为每个客户机都可以定义自己的选择集。在这种情况下，订阅选择集必须是变更选择集的子集。例如，链接到变更 `addPost(...){id author title url version}` 的订阅 `addedPost{author title}` 仅接收文章的作者和标题。它不会接收其他字段。但是，如果变更在其选择集中缺少作者，则订阅者将获得作者字段的 `null` 值（或者，如果在架构中将作者字段定义为必填/非 Null 的情况下，将得到错误）。

使用 pure 时，订阅选择集是必不可少的 WebSockets。如果订阅中未明确定义字段，则 AWS AppSync 不会返回该字段。

在以上示例中，订阅没有参数。假设您的架构如下所示：

```
type Subscription {
    updatedPost(id:ID! author:String): Post
    @aws_subscribe(mutations: ["updatePost"])
}
```

在这种情况下，您的客户端定义了订阅，如下所示：

```
subscription UpdatedPostSub {
    updatedPost(id:"XYZ", author:"ABC") {
        title
        content
    }
}
```

您的架构中 `subscription` 字段的返回类型必须与相应的变更字段的返回类型匹配。在上一示例中，`addPost` 和 `addedPost` 返回的类型都是 `Post`。

要在客户端上设置订阅，请参阅[使用 Amplify 客户端构建客户端应用程序](building-a-client-app.md)。

## 使用订阅参数
<a name="using-subscription-arguments"></a>

使用 GraphQL 订阅的一个重要部分是，了解何时以及如何使用参数。您可以进行细微的更改，以修改何时以及如何向客户端通知发生的变更。为此，请参阅快速入门章节中创建“Todo”的示例架构。对于该示例架构，定义了以下变更：

```
type Mutation {
    createTodo(input: CreateTodoInput!): Todo
    updateTodo(input: UpdateTodoInput!): Todo
    deleteTodo(input: DeleteTodoInput!): Todo
}
```

在默认示例中，客户端可以使用不带参数的 `onUpdateTodo` `subscription` 订阅任何 `Todo` 的更新：

```
subscription OnUpdateTodo {
  onUpdateTodo {
    description
    id
    name
    when
  }
}
```

您可以使用 `subscription` 的参数对其进行筛选。例如，要仅在更新具有特定 `ID` 的 `todo` 时触发 `subscription`，请指定 `ID` 值：

```
subscription OnUpdateTodo {
  onUpdateTodo(id: "a-todo-id") {
    description
    id
    name
    when
  }
}
```

您也可以传递多个参数。例如，以下 `subscription` 说明了如何在特定地点和时间获取任何 `Todo` 更新的通知：

```
subscription todosAtHome {
  onUpdateTodo(when: "tomorrow", where: "at home") {
    description
    id
    name
    when
    where
  }
}
```

请注意，所有参数都是可选的。如果未在 `subscription` 中指定任何参数，您将订阅应用程序中发生的所有 `Todo` 更新。不过，您可以更新 `subscription` 的字段定义以要求使用 `ID` 参数。这会强制提供特定 `todo` 的响应，而不是提供所有 `todo` 的响应：

```
onUpdateTodo(
  id: ID!,
  name: String,
  when: String,
  where: String,
  description: String
): Todo
```

### 参数 null 值具有含义
<a name="argument-null-value-has-meaning"></a>

在中进行订阅查询时 AWS AppSync，`null`参数值对结果的筛选方式与完全省略参数的方式不同。

让我们回到创建 Todo 的 Todo API 示例。请参阅快速入门章节中的示例架构。

让我们修改架构以在 `Todo` 类型上包含新的 `owner` 字段，该字段描述所有者是谁。`owner` 字段不是必填字段，只能在 `UpdateTodoInput` 上设置该字段。请参阅以下简化架构版本：

```
type Todo {
  id: ID!
  name: String!
  when: String!
  where: String!
  description: String!
  owner: String
}

input CreateTodoInput {
  name: String!
  when: String!
  where: String!
  description: String!
}

input UpdateTodoInput {
  id: ID!
  name: String
  when: String
  where: String
  description: String
  owner: String
}

type Subscription {
    onUpdateTodo(
        id: ID,
        name: String,
        when: String,
        where: String,
        description: String
    ): Todo @aws_subscribe(mutations: ["updateTodo"])
}
```

以下订阅返回所有 `Todo` 更新：

```
subscription MySubscription {
  onUpdateTodo {
    description
    id
    name
    when
    where
  }
}
```

如果您修改前面的订阅以添加字段参数 `owner: null`，您现在会问一个不同的问题。该订阅现在注册客户端，以获得所有未提供所有者的 `Todo` 更新的通知。

```
subscription MySubscription {
  onUpdateTodo(owner: null) {
    description
    id
    name
    when
    where
  }
}
```

**注意**  
**自 2022 年 1 月 1 日起，MQTT WebSockets 已不再可用作 GraphQL 订阅的协议。 AWS AppSync APIsPure WebSockets 是中唯一支持的协议 AWS AppSync。**  
基于 2019 年 11 月之后发布的 AWS AppSync SDK 或 Amplify 库的客户 WebSockets 默认会自动使用 pure。将客户端升级到最新版本允许他们使用 AWS AppSync纯 WebSockets 引擎。  
Pur WebSockets e 具有更大的有效载荷大小 (240 KB)、更多的客户端选项和改进的 CloudWatch 指标。有关使用纯 WebSocket 客户端的更多信息，请参阅[在中构建实时 WebSocket 客户端 AWS AppSync](real-time-websocket-client.md)。

# 在中创建由无服务器 pub/sub APIs WebSockets 提供支持的通用部件 AWS AppSync
<a name="aws-appsync-real-time-create-generic-api-serverless-websocket"></a>

**重要**  
从 2025 年 3 月 13 日起， WebSockets 您可以使用 AWS AppSync 事件构建实时 PubSub API。有关更多信息，请参阅[事件开发者指南 WebSocket中的通过发布AWS](https://docs.aws.amazon.com/appsync/latest/eventapi/publish-websocket.html)*AppSync 事件*。

有些应用程序只要求客户收听特定频道或主题的简单 WebSocket APIs 功能。对于没有特定形状或强类型要求的通用 JSON 数据，可以将其推送到使用纯粹且简单的发布-订阅 (Pub/Sub) 模式侦听这些通道之一的客户端。

通过在 API 后端 pub/sub WebSocket APIs 和客户端自动生成 GraphQL 代码，可以在几分钟内轻松实现 GraphQL 知识。 AWS AppSync 

## 创建和配置 pub-sub APIs
<a name="aws-appsync-real-time-enhanced-filtering-using-pub-sub-apis"></a>

首先，执行以下操作：

1. 登录 AWS 管理控制台 并打开[AppSync 控制台](https://console.aws.amazon.com/appsync/)。

   1. 在**控制面板**中，选择**创建 API**。

1. 在下一个屏幕上，选择**创建一个实时 API**，然后选择**下一步**。

1. 输入您的 pub/sub API 的友好名称。

1. 您可以启用[私有 API](https://docs.aws.amazon.com/appsync/latest/devguide/using-private-apis.html) 功能，但我们建议暂时关闭该功能。选择**下一步**。

1. 您可以选择使用自动生成有效的 pub/sub API WebSockets。我们建议暂时也关闭该功能。选择**下一步**。

1. 选择**创建 API**，然后等待几分钟。将在您的账户中创建一个新的预配置 AWS AppSync 发布/订阅 API。 AWS 

API 使用内置 AWS AppSync的本地解析器（有关使用本地解析器的更多信息，请参阅*《AWS AppSync 开发者指南》*中的[教程：本地解析器](https://docs.aws.amazon.com/appsync/latest/devguide/tutorial-local-resolvers-js.html)）来管理多个临时 pub/sub 通道和 WebSocket 连接，它仅根据频道名称自动向订阅的客户端传送和筛选数据。API 调用通过 API 密钥进行授权。

在部署 API 后，您需要执行几个额外的步骤以生成客户端代码，并将其与客户端应用程序集成在一起。本指南使用一个简单的 React Web 应用程序，以提供如何快速集成客户端的示例。

1. 首先，在本地计算机上使用 [NPM](https://www.npmjs.com/get-npm) 创建一个样板 React 应用程序：

   ```
   $ npx create-react-app mypubsub-app 
   $ cd mypubsub-app
   ```
**注意**  
该示例使用 [Amplify 库](https://docs.amplify.aws/lib/)将客户端连接到后端 API。不过，无需在本地创建 Amplify CLI 项目。虽然 React 是该示例中选择的客户端，但 Amplify 库还支持 iOS、Android 和 Flutter 客户端，并在这些不同的运行时系统中提供相同的功能。[支持的Amplify客户端提供了简单的抽象，只需几行代码即可与Gr AWS AppSync aphQL API后端进行交互，包括与实时协议完全兼容的内置 WebSocket功能：AWS AppSync WebSocket ](https://docs.aws.amazon.com/appsync/latest/devguide/real-time-websocket-client.html)  

   ```
   $ npm install @aws-amplify/api
   ```

1. 在 AWS AppSync 控制台中，选择 “下载” **JavaScript**，**下载**包含 API 配置详细信息和生成的 GraphQL 操作代码的单个文件。

1. 将下载的文件复制到 React 项目中的 `/src` 文件夹。

1. 接下来，将现有的样板 `src/App.js` 文件内容替换为控制台中提供的示例客户端代码。

1. 使用以下命令在本地启动该应用程序：

   ```
   $ npm start
   ```

1. 要测试实时数据的发送和接收，请打开两个浏览器窗口并访问*localhost:3000*。示例应用程序配置为将通用 JSON 数据发送到名*robots*为的硬编码频道。

1.  在其中的一个浏览器窗口中，在文本框中输入以下 JSON Blob，然后单击**提交**：

   ```
   {
     "robot":"r2d2",
     "planet": "tatooine"
   }
   ```

两个浏览器实例都订阅了该*robots*频道并实时接收发布的数据，这些数据显示在 Web 应用程序的底部：

![\[适用于 pub/sub API 的 React 应用程序示例\]](http://docs.aws.amazon.com/zh_cn/appsync/latest/devguide/images/pub-sub-react.png)


所有必需的 GraphQL API 代码（包括架构、解析器和操作）都会自动生成，以启用通用 pub/sub 用例。在后端，数据通过 GraphQL 突变发布到 AWS AppSync的实时端点，如下所示：

```
mutation PublishData {
    publish(data: "{\"msg\": \"hello world!\"}", name: "channel") {
        data
        name
    }
}
```

订阅者通过相关的 GraphQL 订阅访问发送到特定临时通道的发布数据：

```
subscription SubscribeToData {
    subscribe(name:"channel") {
        name
        data
    }
}
```

## 在现有应用程序中实现 pub- APIs sub
<a name="aws-appsync-real-time-enhanced-filtering-existing-apps"></a>

如果您只需要在现有应用程序中实现实时功能，则可以轻松地将此通用 pub/sub API 配置集成到任何应用程序或 API 技术中。虽然使用单个 API 端点通过 GraphQL 在单个网络调用中安全地访问、操作和合并来自一个或多个数据源的数据具有优势，但无需从头开始转换或重建基于 REST 的现有应用程序即可利用的 AWS AppSync实时功能。例如，您可以将现有 CRUD 工作负载放在单独的 API 端点中，客户端仅出于实时 pub/sub 目的从现有应用程序向通用 pub/sub API 发送和接收消息或事件。

# 在中定义增强型订阅过滤器 AWS AppSync
<a name="aws-appsync-real-time-enhanced-filtering"></a>

**重要**  
从 2025 年 3 月 13 日起， WebSockets 您可以使用 AWS AppSync 事件构建实时 PubSub API。有关更多信息，请参阅[事件开发者指南 WebSocket中的通过发布AWS](https://docs.aws.amazon.com/appsync/latest/eventapi/publish-websocket.html)*AppSync 事件*。

在中 AWS AppSync，您可以使用支持其他逻辑运算符的筛选器，直接在 GraphQL API 订阅解析器中定义和启用用于在后端进行数据筛选的业务逻辑。您可以配置这些筛选条件，这与在客户端的订阅查询上定义的订阅参数不同。有关使用订阅参数的更多信息，请参阅[使用订阅参数](aws-appsync-real-time-data.md#using-subscription-arguments)。有关运算符列表，请参阅[AWS AppSync 解析器映射模板实用程序参考](resolver-util-reference.md)。

就本文而言，我们将实时数据筛选分为以下几个类别：
+ **基本筛选** - 根据订阅查询中的客户端定义的参数进行筛选。
+ **增强筛选**-基于 AWS AppSync 服务后端集中定义的逻辑进行筛选。

以下几节介绍了如何配置增强订阅筛选条件，并说明了它们的实际用途。

## 在 GraphQL 架构中定义订阅
<a name="aws-appsync-real-time-enhanced-filtering-using-subscription-filters"></a>

要使用增强订阅筛选条件，您可以在 GraphQL 架构中定义订阅，然后使用筛选扩展定义增强筛选条件。要说明增强型订阅筛选的工作原理 AWS AppSync，请使用以下 GraphQL 架构（它定义了票证管理系统 API）作为示例：

```
type Ticket {
	id: ID
	createdAt: AWSDateTime
	content: String
	severity: Int
	priority: Priority
	category: String
	group: String
	status: String
	
}

type Mutation {
	createTicket(input: TicketInput): Ticket
}

type Query {
	getTicket(id: ID!): Ticket
}

type Subscription {
	onSpecialTicketCreated: Ticket @aws_subscribe(mutations: ["createTicket"])
	onGroupTicketCreated(group: String!): Ticket @aws_subscribe(mutations: ["createTicket"])
}



enum Priority {
	none
	lowest
	low
	medium
	high
	highest
}

input TicketInput {
	content: String
	severity: Int
	priority: Priority
	category: String
	group: String
```

假设您为 API 创建一个 `NONE` 数据来源，然后使用该数据来源将一个解析器附加到 `createTicket` 变更。您的处理程序可能如下所示：

```
import { util } from '@aws-appsync/utils';

export function request(ctx) {
	return {
		payload: {
			id: util.autoId(),
			createdAt: util.time.nowISO8601(),
			status: 'pending',
			...ctx.args.input,
		},
	};
}

export function response(ctx) {
	return ctx.result;
}
```

**注意**  
增强筛选条件是在给定订阅的 GraphQL 解析器处理程序中启用的。有关更多信息，请参阅[解析器参考](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-reference-js-version.html)。

要实施增强筛选条件的行为，您必须使用 `extensions.setSubscriptionFilter()` 函数定义一个筛选条件表达式，该表达式针对通过 GraphQL 变更发布并且订阅客户端可能感兴趣的数据进行评估。有关筛选扩展的更多信息，请参阅[扩展](https://docs.aws.amazon.com//appsync/latest/devguide/extensions-js.html)。

以下几节介绍了如何使用筛选扩展实施增强筛选条件。

## 使用筛选扩展创建增强订阅筛选条件
<a name="aws-appsync-real-time-enhanced-filtering-defining-filters"></a>

增强筛选条件是在订阅解析器的响应处理程序中以 JSON 格式编写的。可以将筛选条件组合到一个名为 `filterGroup` 的列表中。筛选条件是使用至少一个规则定义的，每个规则包含字段、运算符和值。让我们为 `onSpecialTicketCreated` 定义一个新的解析器以设置增强筛选条件。您可以在一个筛选条件中配置多个使用 AND 逻辑进行评估的规则，而一个筛选条件组中的多个筛选条件使用 OR 逻辑进行评估：

```
import { util, extensions } from '@aws-appsync/utils';

export function request(ctx) {
	// simplfy return null for the payload
	return { payload: null };
}

export function response(ctx) {
	const filter = {
		or: [
			{ severity: { ge: 7 }, priority: { in: ['high', 'medium'] } },
			{ category: { eq: 'security' }, group: { in: ['admin', 'operators'] } },
		],
	};
	extensions.setSubscriptionFilter(util.transform.toSubscriptionFilter(filter));

  // important: return null in the response
	return null;
}
```

根据前面示例中定义的筛选条件，如果使用以下条件创建了票证，则将重要票证自动推送到订阅的 API 客户端：
+ `priority` 级别 `high` 或 `medium`

  AND 
+ 大于或等于 `7` (`ge`) 的 `severity` 级别

或 
+ 设置为 `Security` 的 `classification` 票证 

  AND 
+ 设置为 `admin` 或 `operators` 的 `group` 分配

![\[显示票证筛选查询的示例\]](http://docs.aws.amazon.com/zh_cn/appsync/latest/devguide/images/aws-priority-example.png)


订阅解析器中定义的筛选条件（增强筛选）优先于仅基于订阅参数的筛选（基本筛选）。有关使用订阅参数的更多信息，请参阅[使用订阅参数](https://docs.aws.amazon.com//appsync/latest/devguide/aws-appsync-real-time-data.html#using-subscription-arguments)。

如果在订阅的 GraphQL 架构中定义某个参数，并且该参数是必需的，只有在解析器的 `extensions.setSubscriptionFilter()` 方法中将给定参数定义为规则时，才会根据该参数进行筛选。不过，如果在订阅解析器中没有 `extensions` 筛选方法，则客户端中定义的参数仅用于基本筛选。您不能同时使用基本筛选和增强筛选。

您可以在订阅的筛选条件扩展逻辑中使用 [`context` 变量](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-context-reference-js.html)，以访问有关请求的上下文信息。例如，在使用 Amazon Cognito 用户池、OIDC 或 Lambda 自定义授权者进行授权时，您可以在设置订阅后在 `context.identity` 中检索有关您的用户的信息。您可以使用该信息根据用户身份设置筛选条件。

现在假设您希望实施 `onGroupTicketCreated` 的增强筛选条件行为。`onGroupTicketCreated` 订阅要求将必需的 `group` 名称作为参数。在创建后，将自动为票证分配 `pending` 状态。您可以设置订阅筛选条件，以仅接收属于提供的组的新创建的票证：

```
import { util, extensions } from '@aws-appsync/utils';

export function request(ctx) {
	// simplfy return null for the payload
	return { payload: null };
}

export function response(ctx) {
	const filter = { group: { eq: ctx.args.group }, status: { eq: 'pending' } };
	extensions.setSubscriptionFilter(util.transform.toSubscriptionFilter(filter));

	return null;
}
```

在使用变更发布数据时，如以下示例中所示：

```
mutation CreateTicket {
  createTicket(input: {priority: medium, severity: 2, group: "aws"}) {
    id
    priority
    severity
    status
    group
    createdAt
  }
}
```

订阅的客户端会在创建带有`createTicket`变异的票证后立即监听要自动推送 WebSockets的数据：

```
subscription OnGroup {
  onGroupTicketCreated(group: "aws") {
    category
    status
    severity
    priority
    id
    group
    createdAt
    content
  }
}
```

无需参数即可订阅客户端，因为筛选逻辑是在 AWS AppSync 服务中实现的，具有增强筛选功能，从而简化了客户端代码。只有在满足定义的筛选条件时，客户端才会收到数据。

## 为嵌套的架构字段定义增强筛选条件
<a name="aws-appsync-real-time-enhanced-filters-nested-schema-fields.title"></a>

您可以使用增强订阅筛选以筛选嵌套的架构字段。假设我们修改了上一节中的架构以包含位置和地址类型：

```
type Ticket {
	id: ID
	createdAt: AWSDateTime
	content: String
	severity: Int
	priority: Priority
	category: String
	group: String
	status: String
	location: ProblemLocation
}

type Mutation {
	createTicket(input: TicketInput): Ticket
}

type Query {
	getTicket(id: ID!): Ticket
}

type Subscription {
	onSpecialTicketCreated: Ticket @aws_subscribe(mutations: ["createTicket"])
	onGroupTicketCreated(group: String!): Ticket @aws_subscribe(mutations: ["createTicket"])
}

type ProblemLocation {
	address: Address
}

type Address {
	country: String
}

enum Priority {
	none
	lowest
	low
	medium
	high
	highest
}

input TicketInput {
	content: String
	severity: Int
	priority: Priority
	category: String
	group: String
	location: AWSJSON
```

对于该架构，您可以使用 `.` 分隔符表示嵌套。以下示例为 `location.address.country` 下面的嵌套架构字段添加筛选规则。如果票证的地址设置为 `USA`，则会触发订阅：

```
import { util, extensions } from '@aws-appsync/utils';

export const request = (ctx) => ({ payload: null });

export function response(ctx) {
	const filter = {
		or: [
			{ severity: { ge: 7 }, priority: { in: ['high', 'medium'] } },
			{ category: { eq: 'security' }, group: { in: ['admin', 'operators'] } },
			{ 'location.address.country': { eq: 'USA' } },
		],
	};
	extensions.setSubscriptionFilter(util.transform.toSubscriptionFilter(filter));
	return null;
}
```

在上面的示例中，`location` 表示嵌套级别 1，`address` 表示嵌套级别 2，`country` 表示嵌套级别 3，所有这些字段以 `.` 分隔符分隔。

您可以使用 `createTicket` 变更测试该订阅：

```
mutation CreateTicketInUSA {
  createTicket(input: {location: "{\"address\":{\"country\":\"USA\"}}"}) {
    category
    content
    createdAt
    group
    id
    location {
      address {
        country
      }
    }
    priority
    severity
    status
  }
}
```

## 从客户端中定义增强筛选条件
<a name="aws-appsync-real-time-enhanced-filtering-defining-from-client"></a>

您可以在 GraphQL 中通过[订阅参数](https://docs.aws.amazon.com/appsync/latest/devguide/aws-appsync-real-time-data.html#using-subscription-arguments)使用基本筛选。在订阅查询中进行调用的客户端定义参数的值。当在带有筛选功能的 AWS AppSync 订阅解析器中启用增强型`extensions`过滤器时，解析器中定义的后端过滤器具有优先级和优先级。

使用 `filter` 参数在订阅中配置客户端定义的动态增强筛选条件。在配置这些筛选条件时，您必须更新 GraphQL 架构以反映新参数：

```
...
type Subscription {
    onSpecialTicketCreated(filter: String): Ticket
        @aws_subscribe(mutations: ["createTicket"])
}
...
```

然后，客户端可以发送订阅查询，如以下示例中所示：

```
subscription onSpecialTicketCreated($filter: String) {
     onSpecialTicketCreated(filter: $filter) {
        id
        group
        description
        priority
        severity
     }
 }
```

您可以像以下示例一样配置查询变量：

```
{"filter" : "{\"severity\":{\"le\":2}}"}
```

可以在订阅响应映射模板中实施 `util.transform.toSubscriptionFilter()` 解析器实用程序，以应用每个客户端的订阅参数中定义的筛选条件：

```
import { util, extensions } from '@aws-appsync/utils';

export function request(ctx) {
	// simplfy return null for the payload
	return { payload: null };
}

export function response(ctx) {
	const filter = ctx.args.filter;
	extensions.setSubscriptionFilter(util.transform.toSubscriptionFilter(filter));
	return null;
}
```

通过使用该策略，客户端可以定义自己的筛选条件，以使用增强筛选逻辑和额外的运算符。当给定客户端在安全 WebSocket 连接中调用订阅查询时，就会分配过滤器。有关用于增强筛选的转换实用程序的更多信息，包括`filter`查询变量负载的格式，请参阅[JavaScript解析器概述](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-reference-overview-js.html)。

## 额外的增强筛选限制
<a name="aws-appsync-real-time-enhanced-filtering-additional-restrictions"></a>

以下是几个对增强筛选条件施加额外限制的使用案例：
+ 增强筛选条件不支持顶级对象列表筛选。在该使用案例中，对于增强订阅，将忽略来自变更的发布数据。
+ AWS AppSync 支持多达五个嵌套级别。将忽略超过嵌套级别 5 的架构字段的筛选条件。请考虑下面的 GraphQL 响应。允许使用 `venue.address.country.metadata.continent` 中的 `continent` 字段，因为它是 5 级嵌套。不过，`venue.address.country.metadata.capital.financial` 中的 `financial` 是 6 级嵌套，因此，筛选条件不起作用：

  ```
  {
      "data": {
          "onCreateFilterEvent": {
              "venue": {
                  "address": {
                      "country": {
                          "metadata": {
                              "capital": {
                                  "financial": "New York"
                              },
                              "continent" : "North America"
                          }
                      },
                      "state": "WA"
                  },
                  "builtYear": 2023
              },
              "private": false,
          }
      }
  }
  ```

# 使用中的过滤器取消订阅 WebSocket 连接 AWS AppSync
<a name="aws-appsync-real-time-invalidation"></a>

**重要**  
从 2025 年 3 月 13 日起， WebSockets 您可以使用 AWS AppSync 事件构建实时 PubSub API。有关更多信息，请参阅[事件开发者指南 WebSocket中的通过发布AWS](https://docs.aws.amazon.com/appsync/latest/eventapi/publish-websocket.html)*AppSync 事件*。

在中 AWS AppSync，您可以根据特定的筛选逻辑强制取消订阅并关闭（使 WebSocket连接失效）来自已连接客户端的连接。这在与授权相关的场景中非常有用，例如，在您从组中删除用户时。

订阅失效是针对变更中定义的负载做出的响应。我们建议您将用于使订阅连接失效的变更视为 API 中的管理操作，并仅限管理员用户、组或后端服务使用这些变更以相应地设置其权限范围。例如，使用架构授权指令，例如 `@aws_auth(cognito_groups: ["Administrators"])` 或 `@aws_iam`。有关更多信息，请参阅[使用其他授权模式](https://docs.aws.amazon.com/appsync/latest/devguide/security-authz.html#using-additional-authorization-modes)。

失效筛选条件使用与[增强订阅筛选条件](https://docs.aws.amazon.com/appsync/latest/devguide/aws-appsync-real-time-enhanced-filtering.html)相同的语法和逻辑。请使用以下实用程序定义这些筛选条件：
+ `extensions.invalidateSubscriptions()` - 在变更的 GraphQL 解析器响应处理程序中定义。
+ `extensions.setSubscriptionInvalidationFilter()` - 在链接到变更的订阅的 GraphQL 解析器响应处理程序中定义。

有关失效筛选扩展插件的更多信息，请参阅[JavaScript解析器](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-reference-overview-js.html)概述。

## 使用订阅失效
<a name="aws-appsync-real-time-invalidation-using-invalidations"></a>

要查看订阅失效的工作原理 AWS AppSync，请使用以下 GraphQL 架构：

```
type User {
  userId: ID!
  groupId: ID!
}
    
type Group {
  groupId: ID!
  name: String!
  members: [ID!]!
}

type GroupMessage {
  userId: ID!
  groupId: ID!
  message: String!
}

type Mutation {
    createGroupMessage(userId: ID!, groupId : ID!, message: String!): GroupMessage
    removeUserFromGroup(userId: ID!, groupId : ID!) : User @aws_iam
}

type Subscription {
    onGroupMessageCreated(userId: ID!, groupId : ID!): GroupMessage
        @aws_subscribe(mutations: ["createGroupMessage"])
}

type Query {
	none: String
}
```

在 `removeUserFromGroup` 变更解析器代码中定义失效筛选条件：

```
import { extensions } from '@aws-appsync/utils';

export function request(ctx) {
	return { payload: null };
}

export function response(ctx) {
	const { userId, groupId } = ctx.args;
	extensions.invalidateSubscriptions({
		subscriptionField: 'onGroupMessageCreated',
		payload: { userId, groupId },
	});
	return { userId, groupId };
}
```

在调用变更时，`payload` 对象中定义的数据用于取消订阅 `subscriptionField` 中定义的订阅。还会在 `onGroupMessageCreated` 订阅的响应映射模板中定义一个失效筛选条件。

如果`extensions.invalidateSubscriptions()`有效载荷包含的 ID 与过滤器中定义的 IDs 来自已订阅客户端的 ID 相匹配，则相应的订阅将被取消订阅。此外， WebSocket 连接已关闭。定义 `onGroupMessageCreated` 订阅的订阅解析器代码：

```
import { util, extensions } from '@aws-appsync/utils';

export function request(ctx) {
	// simplfy return null for the payload
	return { payload: null };
}

export function response(ctx) {
	const filter = { groupId: { eq: ctx.args.groupId } };
	extensions.setSubscriptionFilter(util.transform.toSubscriptionFilter(filter));

	const invalidation = { groupId: { eq: ctx.args.groupId }, userId: { eq: ctx.args.userId } };
	extensions.setSubscriptionInvalidationFilter(util.transform.toSubscriptionFilter(invalidation));

	return null;
}
```

请注意，订阅响应处理程序可以同时定义订阅筛选条件和失效筛选条件。

例如，假设客户端 A 使用以下订阅请求为具有 ID `user-1` 的新用户订阅具有 ID `group-1` 的组：

```
onGroupMessageCreated(userId : "user-1", groupId: :"group-1"){...}
```

AWS AppSync 运行订阅解析器，该解析器生成前面的`onGroupMessageCreated`响应映射模板中定义的订阅和失效过滤器。对于客户端 A，订阅筛选条件仅允许将数据发送到 `group-1`，并为 `user-1` 和 `group-1` 定义了失效筛选条件。

现在假设客户端 B 使用以下订阅请求为具有 ID `user-2` 的用户订阅具有 ID `group-2` 的组：

```
onGroupMessageCreated(userId : "user-2", groupId: :"group-2"){...}
```

AWS AppSync 运行订阅解析器，它会生成订阅和失效过滤器。对于客户端 B，订阅筛选条件仅允许将数据发送到 `group-2`，并为 `user-2` 和 `group-2` 定义了失效筛选条件。

接下来，假设使用变更请求创建 ID 为 `message-1` 的新组消息，如以下示例中所示：

```
createGroupMessage(id: "message-1", groupId :
      "group-1", message: "test message"){...}
```

与定义的过滤器匹配的订阅客户端会自动通过 WebSockets以下方式接收以下数据负载：

```
{
  "data": {
    "onGroupMessageCreated": {
      "id": "message-1",
      "groupId": "group-1",
      "message": "test message",
    }
  }
}
```

客户端 A 收到该消息，因为筛选条件与定义的订阅筛选条件匹配。不过，客户端 B 不会收到该消息，因为该用户不属于 `group-1`。此外，请求与订阅解析器中定义的订阅筛选条件不匹配。

最后，假设使用以下变更请求从 `group-1` 中删除了 `user-1`：

```
removeUserFromGroup(userId: "user-1", groupId : "group-1"){...}
```

该突变会启动订阅失效，如其`extensions.invalidateSubscriptions()`解析器响应处理程序代码中所定义。 AWS AppSync 然后取消订阅客户端 A 并关闭其连接。 WebSocket 客户端 B 不受影响，因为变更中定义的失效负载与其用户或组不匹配。

当连接 AWS AppSync 失效时，客户端会收到一条消息，确认他们已取消订阅：

```
{
  "message": "Subscription complete."
}
```

## 在订阅失效筛选条件中使用上下文变量
<a name="aws-appsync-real-time-invalidation-context"></a>

与增强订阅筛选条件一样，您可以在订阅失效筛选条件扩展中使用 [`context` 变量](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-context-reference-js.html)以访问某些数据。

例如，可以在变更中将电子邮件地址配置为失效负载，然后将其与通过 Amazon Cognito 用户池或 OpenID Connect 授权的订阅用户的电子邮件属性或声明进行匹配。失效筛选条件（在 `extensions.setSubscriptionInvalidationFilter()` 订阅失效器中定义）检查变更的 `extensions.invalidateSubscriptions()` 负载设置的电子邮件地址是否与从用户 JWT 令牌的 `context.identity.claims.email` 中检索的电子邮件地址匹配，从而启动失效。

# 在中构建实时 WebSocket 客户端 AWS AppSync
<a name="real-time-websocket-client"></a>

**重要**  
从 2025 年 3 月 13 日起， WebSockets 您可以使用 AWS AppSync 事件构建实时 PubSub API。有关更多信息，请参阅《[事件开发者指南》 WebSocket中的通过发布AWS](https://docs.aws.amazon.com/appsync/latest/eventapi/publish-websocket.html)*AppSync 事件*。

AWS AppSync的实时 WebSocket 客户端通过多步流程支持 GraphQL 订阅。客户端首先与 AWS AppSync 实时端点建立 WebSocket 连接，发送连接初始化消息，然后等待确认。成功连接后，客户端通过发送带有唯一查询 IDs 和 GraphQL 查询的启动消息来注册订阅。 AWS AppSync 使用确认消息确认订阅成功。然后客户端侦听订阅事件，这些事件由相应的变更触发。为了保持连接， AWS AppSync 会定期发送保持活动状态的消息。完成后，客户端通过发送停止消息来取消注册订阅。该系统支持在单个 WebSocket 连接上进行多个订阅，并支持各种授权模式，包括 API 密钥、Amazon Cognito 用户池、IAM 和 Lambda。

## Graph WebSocket QL 订阅的实时客户端实现
<a name="appsynclong-real-time-websocket-client-implementation-guide-for-graphql-subscriptions"></a>

以下序列图和步骤显示了 WebSocket 客户端、HTTP 客户端和之间的实时订阅工作流程 AWS AppSync。

![\[Sequence diagram showing WebSocket client, AppSync endpoints, and HTTP client interactions for real-time subscriptions.\]](http://docs.aws.amazon.com/zh_cn/appsync/latest/devguide/images/realtime-client-flow.png)


1. 客户端与 AWS AppSync 实时端点建立 WebSocket 连接。如果存在网络错误，则客户端应该执行抖动指数退避。有关更多信息，请参阅架构[博客上的指数退避和抖动](https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/)。 AWS 

1. （可选）成功建立 WebSocket 连接后，客户端发送一条`connection_init`消息。

1. 如果`connection_init`已发送，则客户端将等待来自 AWS AppSync的`connection_ack`消息。该消息包含一个 `connectionTimeoutMs` 参数，它是 `"ka"`（保持活动）消息的最长等待时间（以毫秒为单位）。

1. AWS AppSync 定期发送`"ka"`消息。客户端跟踪它收到每条 `"ka"` 消息的时间。如果客户端在 `connectionTimeoutMs` 毫秒内未收到 `"ka"` 消息，客户端应关闭连接。

1. 客户端通过发送 `start` 订阅消息来注册订阅。单个 WebSocket 连接支持多个订阅，即使它们处于不同的授权模式。

1. 客户端等待发送`start_ack`消息 AWS AppSync 以确认订阅成功。如果出现错误，则 AWS AppSync 返回一条`"type": "error"`消息。

1. 客户端侦听订阅事件，这些事件是在调用相应变更后发送的。查询和突变通常会发送`https://`到 AWS AppSync GraphQL 端点。订阅通过 secure WebSocket (`wss://`) 流经 AWS AppSync 实时端点。

1. 客户端通过发送 `stop` 订阅消息来取消注册订阅。

1. 取消注册所有订阅并检查没有消息通过传输后 WebSocket，客户端可以断开与 WebSocket 连接的连接。

## 握手详情以建立连接 WebSocket
<a name="handshake-details-to-establish-the-websocket-connection"></a>

要与连接并成功启动握手 AWS AppSync， WebSocket 客户端需要满足以下条件：
+  AWS AppSync 实时终端节点
+ 标头-包含与 AWS AppSync 端点和授权相关的信息。 AWS AppSync 支持以下三种提供标头的方法：
  + 通过查询字符串提供标头
    + 标头信息编码为 base64 字符串，该字符串源自字符串化 JSON 对象。此 JSON 对象包含与 AWS AppSync 端点和授权相关的详细信息。JSON 对象的内容因授权模式而异。
  + 通过 `Sec-WebSocket-Protocol` 提供标头
    + 来自字符串化的 JSON 对象的 Base64URL 编码字符串作为协议在标头中传递，该字符串包含与 AWS AppSync端点和授权相关的信息。`Sec-WebSocket-Protocol`JSON 对象的内容因授权模式而异。
  + 通过标准 HTTP 标头提供标头：
    + 标头可以在连接请求中作为标准 HTTP 标头传递，类似于 GraphQL 查询和变更时传递标头的方式。 AWS AppSync但是，私有 API 连接请求不支持通过标准 HTTP 标头传递标头。
+  `payload` - `payload` 的 Base64 编码字符串。仅当使用查询字符串提供标头时，才需要有效载荷

根据这些要求， WebSocket 客户端可以使用`graphql-ws`作为 WebSocket 协议连接到 URL，该网址包含带有查询字符串的实时端点。

### 从 GraphQL 终端节点发现 实时终端节点
<a name="discovering-the-appsync-real-time-endpoint-from-the-appsync-graphql-endpoint"></a>

 AWS AppSync GraphQL 端点和 AWS AppSync 实时端点在协议和域上略有不同。您可以使用 AWS Command Line Interface (AWS CLI) 命令检索 GraphQL 端点。`aws appsync get-graphql-api`

****AWS AppSync GraphQL 端点：****  
 `https://example1234567890000.appsync-api.us-east-1.amazonaws.com/graphql`

****AWS AppSync 实时端点：****  
 `wss://example1234567890000.appsync-realtime-api.us-east-1.amazonaws.com/graphql`

应用程序可以使用任何 HTTP 客户端连接到 AWS AppSync GraphQL 端点 (`https://`) 进行查询和变更。应用程序可以使用任何 WebSocket 客户端连接到 AWS AppSync 实时终端节点 (`wss://`) 进行订阅。

通过使用自定义域名，您可以使用单个域与两个终端节点进行交互。例如，如果您配置`api.example.com`为自定义域，则可以使用以下方式与GraphQL和实时终端节点进行交互： URLs

**AWS AppSync 自定义域 GraphQL 端点：**  
`https://api.example.com/graphql`

**AWS AppSync 自定义域名实时终端节点：**  
`wss://api.example.com/graphql/realtime`

## 基于 AWS AppSync API 授权模式的标头参数格式
<a name="header-parameter-format-based-on-appsync-api-authorization-mode"></a>

连接查询字符串中使用的`header`对象的格式因 AWS AppSync API 授权模式而异。对象中的`host`字段指的是 AWS AppSync GraphQL 端点，即使`wss://`调用是针对实时端点进行的，该端点也用于验证连接。要启动握手并建立授权连接，`payload` 应为空 JSON 对象。仅当通过查询字符串传递标头时，才需要有效载荷。

以下各节说明了每种授权模式的标头格式。

### API 密钥
<a name="api-key"></a>

#### API 密钥标头
<a name="api-key-list"></a>

**标头内容**
+  `"host": <string>`： AWS AppSyncGraphQL 终端节点的主机或您的自定义域名。
+  `"x-api-key": <string>`：为 API 配置的 AWS AppSync API 密钥。

**示例**

```
{
    "host":"example1234567890000.appsync-api.us-east-1.amazonaws.com",
    "x-api-key":"da2-12345678901234567890123456"
}
```

**通过查询字符串提供标头**

首先，将包含 `host` 和 `x-api-key` 的 JSON 对象转换为字符串。接下来，使用 base64 编码对该字符串进行编码。生成的 base64 编码字符串作为名为的查询参数添加`header`到 WebSocket URL 中，用于与实时端点建立连接。 AWS AppSync 生成的请求 URL 采用以下形式：

```
wss://example1234567890000.appsync-realtime-api.us-east-1.amazonaws.com/graphql?header=eyJob3N0IjoiZXhhbXBsZTEyMzQ1Njc4OTAwMDAuYXBwc3luYy1hcGkudXMtZWFzdC0xLmFtYXpvbmF3cy5jb20iLCJ4LWFtei1kYXRlIjoiMjAyMDA0MDFUMDAxMDEwWiIsIngtYXBpLWtleSI6ImRhMi16NHc0NHZoczV6Z2MzZHRqNXNranJsbGxqaSJ9&payload=e30=
```

值得注意的是，除了 base64 编码的标头对象外，一个空的 JSON 对象 \$1\$1 也经过了 base64 编码，并作为单独的查询参数包含在网址中命名。`payload` WebSocket

**通过 `Sec-WebSocket-Protocol` 提供标头**

包含 `host` 和 `x-api-key` 的 JSON 对象会转换为字符串，然后使用 base64Url 编码进行编码。生成的 base64Url 编码字符串的前缀为 `header-`。然后，在与 AWS AppSync 实时端点建立 WebSocket 连接时，除了`Sec-WebSocket-Protocol`标头外，还会将`graphql-ws`此前缀字符串用作新的子协议。

生成的请求 URL 采用以下形式：

```
wss://example1234567890000.appsync-realtime-api.us-east-1.amazonaws.com/graphql
```

`Sec-WebSocket-Protocol` 标头包含以下值：

```
"sec-websocket-protocol" : ["graphql-ws", "header-ewogICAgImhvc3QiOiJleGFtcGxlMTIzNDU2Nzg5MDAwMC5hcHBzeW5jLWFwaS51cy1lYXN0LTEuYW1hem9uYXdzLmNvbSIsCiAgICAieC1hcGkta2V5IjoiZGEyLTEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Igp9"]
```

**通过标准 HTTP 标头提供标头**

在这种方法中，在与 AWS AppSync 实时端点建立 WebSocket 连接时，使用标准 HTTP 标头传输主机和 API 密钥信息。生成的请求 URL 采用以下形式：

```
wss://example1234567890000.appsync-realtime-api.us-east-1.amazonaws.com/graphql
```

请求标头将包括以下内容：

```
"sec-websocket-protocol" : ["graphql-ws"]
"host":"example1234567890000.appsync-api.us-east-1.amazonaws.com",
"x-api-key":"da2-12345678901234567890123456"
```

### Amazon Cognito 用户池和 OpenID Connect (OIDC)
<a name="amazon-cognito-user-pools-and-openid-connect-oidc"></a>

#### 亚马逊 Cognito 和 OIDC 标题
<a name="amazon-cognito-user-pools-and-openid-connect-oidc-list"></a>

标头内容：
+  `"Authorization": <string>`：JWT ID 令牌。标头可以使用[持有者方案](https://datatracker.ietf.org/doc/html/rfc6750#section-2.1)。
+  `"host": <string>`： AWS AppSyncGraphQL 终端节点的主机或您的自定义域名。

示例：

```
{
    "Authorization":"eyEXAMPLEiJjbG5xb3A5eW5MK09QYXIrMTJHWEFLSXBieU5WNHhsQjEXAMPLEnM2WldvPSIsImFsZyI6IlEXAMPLEn0.eyEXAMPLEiJhNmNmMjcwNy0xNjgxLTQ1NDItOWYxOC1lNjY0MTg2NjlkMzYiLCJldmVudF9pZCI6ImVkMzM5MmNkLWNjYTMtNGM2OC1hNDYyLTJlZGI3ZTNmY2FjZiIsInRva2VuX3VzZSI6ImFjY2VzcyIsInNjb3BlIjoiYXdzLmNvZ25pdG8uc2lnbmluLnVzZXIuYWRtaW4iLCJhdXRoX3RpbWUiOjE1Njk0NTc3MTgsImlzcyI6Imh0dHBzOlwvXC9jb2duaXRvLWlkcC5hcC1zb3V0aGVhc3QtMi5hbWF6b25hd3MuY29tXC9hcC1zb3V0aGVhc3QtMl83OHY0SVZibVAiLCJleHAiOjE1Njk0NjEzMjAsImlhdCI6MTU2OTQ1NzcyMCwianRpIjoiNTgzZjhmYmMtMzk2MS00YzA4LWJhZTAtYzQyY2IxMTM5NDY5IiwiY2xpZW50X2lkIjoiM3FlajVlMXZmMzd1N3RoZWw0dG91dDJkMWwiLCJ1c2VybmFtZSI6ImVsb3EXAMPLEn0.B4EXAMPLEFNpJ6ikVp7e6DRee95V6Qi-zEE2DJH7sHOl2zxYi7f-SmEGoh2AD8emxQRYajByz-rE4Jh0QOymN2Ys-ZIkMpVBTPgu-TMWDyOHhDUmUj2OP82yeZ3wlZAtr_gM4LzjXUXmI_K2yGjuXfXTaa1mvQEBG0mQfVd7SfwXB-jcv4RYVi6j25qgow9Ew52ufurPqaK-3WAKG32KpV8J4-Wejq8t0c-yA7sb8EnB551b7TU93uKRiVVK3E55Nk5ADPoam_WYE45i3s5qVAP_-InW75NUoOCGTsS8YWMfb6ecHYJ-1j-bzA27zaT9VjctXn9byNFZmEXAMPLExw",
    "host":"example1234567890000.appsync-api.us-east-1.amazonaws.com"
}
```

**通过查询字符串提供标头**

首先，将包含 `host` 和 `Authorization` 的 JSON 对象转换为字符串。接下来，使用 base64 编码对该字符串进行编码。生成的 base64 编码字符串作为名为的查询参数添加`header`到 WebSocket URL 中，用于与实时端点建立连接。 AWS AppSync生成的请求 URL 采用以下形式：

```
wss://example1234567890000.appsync-realtime-api.us-east-1.amazonaws.com/graphql?header=eyJBdXRob3JpemF0aW9uIjoiZXlKcmFXUWlPaUpqYkc1eGIzQTVlVzVNSzA5UVlYSXJNVEpIV0VGTFNYQmllVTVXTkhoc1FqaFBWVzlZTW5NMldsZHZQU0lzSW1Gc1p5STZJbEpUTWpVMkluMC5leUp6ZFdJaU9pSmhObU5tTWpjd055MHhOamd4TFRRMU5ESXRPV1l4T0MxbE5qWTBNVGcyTmpsa016WWlMQ0psZG1WdWRGOXBaQ0k2SW1Wa016TTVNbU5rTFdOallUTXROR00yT0MxaE5EWXlMVEpsWkdJM1pUTm1ZMkZqWmlJc0luUnZhMlZ1WDNWelpTSTZJbUZqWTJWemN5SXNJbk5qYjNCbElqb2lZWGR6TG1OdloyNXBkRzh1YzJsbmJtbHVMblZ6WlhJdVlXUnRhVzRpTENKaGRYUm9YM1JwYldVaU9qRTFOamswTlRjM01UZ3NJbWx6Y3lJNkltaDBkSEJ6T2x3dlhDOWpiMmR1YVhSdkxXbGtjQzVoY0MxemIzVjBhR1ZoYzNRdE1pNWhiV0Y2YjI1aGQzTXVZMjl0WEM5aGNDMXpiM1YwYUdWaGMzUXRNbDgzT0hZMFNWWmliVkFpTENKbGVIQWlPakUxTmprME5qRXpNakFzSW1saGRDSTZNVFUyT1RRMU56Y3lNQ3dpYW5ScElqb2lOVGd6WmpobVltTXRNemsyTVMwMFl6QTRMV0poWlRBdFl6UXlZMkl4TVRNNU5EWTVJaXdpWTJ4cFpXNTBYMmxrSWpvaU0zRmxhalZsTVhabU16ZDFOM1JvWld3MGRHOTFkREprTVd3aUxDSjFjMlZ5Ym1GdFpTSTZJbVZzYjNKNllXWmxJbjAuQjRjZEp0aDNLRk5wSjZpa1ZwN2U2RFJlZTk1VjZRaS16RUUyREpIN3NIT2wyenhZaTdmLVNtRUdvaDJBRDhlbXhRUllhakJ5ei1yRTRKaDBRT3ltTjJZcy1aSWtNcFZCVFBndS1UTVdEeU9IaERVbVVqMk9QODJ5ZVozd2xaQXRyX2dNNEx6alhVWG1JX0syeUdqdVhmWFRhYTFtdlFFQkcwbVFmVmQ3U2Z3WEItamN2NFJZVmk2ajI1cWdvdzlFdzUydWZ1clBxYUstM1dBS0czMktwVjhKNC1XZWpxOHQwYy15QTdzYjhFbkI1NTFiN1RVOTN1S1JpVlZLM0U1NU5rNUFEUG9hbV9XWUU0NWkzczVxVkFQXy1Jblc3NU5Vb09DR1RzUzhZV01mYjZlY0hZSi0xai1iekEyN3phVDlWamN0WG45YnlORlptS0xwQTJMY3h3IiwiaG9zdCI6ImV4YW1wbGUxMjM0NTY3ODkwMDAwLmFwcHN5bmMtYXBpLnVzLWVhc3QtMS5hbWF6b25hd3MuY29tIn0=&payload=e30=
```

值得注意的是，除了 base64 编码的标头对象外，一个空的 JSON 对象 \$1\$1 也经过了 base64 编码，并作为单独的查询参数包含在网址中命名。`payload` WebSocket

**通过 `Sec-WebSocket-Protocol` 提供标头**

包含 `host` 和 `Authorization` 的 JSON 对象会转换为字符串，然后使用 base64Url 编码进行编码。生成的 base64Url 编码字符串的前缀为 `header-`。然后，在与 AWS AppSync 实时端点建立 WebSocket 连接时，除了`Sec-WebSocket-Protocol`标头外，还会将`graphql-ws`此前缀字符串用作新的子协议。

生成的请求 URL 采用以下形式：

```
wss://example1234567890000.appsync-realtime-api.us-east-1.amazonaws.com/graphql
```

`Sec-WebSocket-Protocol` 标头包含以下值：

```
"sec-websocket-protocol" : ["graphql-ws", "header-ewogICAgImhvc3QiOiJleGFtcGxlMTIzNDU2Nzg5MDAwMC5hcHBzeW5jLWFwaS51cy1lYXN0LTEuYW1hem9uYXdzLmNvbSIsCiAgICAieC1hcGkta2V5IjoiZGEyLTEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Igp9"]
```

**通过标准 HTTP 标头提供标头**

在这种方法中，在与 AWS AppSync 实时端点建立 WebSocket 连接时，使用标准 HTTP 标头传输主机和授权信息。生成的请求 URL 采用以下形式：

```
wss://example1234567890000.appsync-realtime-api.us-east-1.amazonaws.com/graphql
```

请求标头将包括以下内容：

```
"sec-websocket-protocol" : ["graphql-ws"]
"Authorization":"eyEXAMPLEiJjbG5xb3A5eW5MK09QYXIrMTJHWEFLSXBieU5WNHhsQjEXAMPLEnM2WldvPSIsImFsZyI6IlEXAMPLEn0.eyEXAMPLEiJhNmNmMjcwNy0xNjgxLTQ1NDItOWYxOC1lNjY0MTg2NjlkMzYiLCJldmVudF9pZCI6ImVkMzM5MmNkLWNjYTMtNGM2OC1hNDYyLTJlZGI3ZTNmY2FjZiIsInRva2VuX3VzZSI6ImFjY2VzcyIsInNjb3BlIjoiYXdzLmNvZ25pdG8uc2lnbmluLnVzZXIuYWRtaW4iLCJhdXRoX3RpbWUiOjE1Njk0NTc3MTgsImlzcyI6Imh0dHBzOlwvXC9jb2duaXRvLWlkcC5hcC1zb3V0aGVhc3QtMi5hbWF6b25hd3MuY29tXC9hcC1zb3V0aGVhc3QtMl83OHY0SVZibVAiLCJleHAiOjE1Njk0NjEzMjAsImlhdCI6MTU2OTQ1NzcyMCwianRpIjoiNTgzZjhmYmMtMzk2MS00YzA4LWJhZTAtYzQyY2IxMTM5NDY5IiwiY2xpZW50X2lkIjoiM3FlajVlMXZmMzd1N3RoZWw0dG91dDJkMWwiLCJ1c2VybmFtZSI6ImVsb3EXAMPLEn0.B4EXAMPLEFNpJ6ikVp7e6DRee95V6Qi-zEE2DJH7sHOl2zxYi7f-SmEGoh2AD8emxQRYajByz-rE4Jh0QOymN2Ys-ZIkMpVBTPgu-TMWDyOHhDUmUj2OP82yeZ3wlZAtr_gM4LzjXUXmI_K2yGjuXfXTaa1mvQEBG0mQfVd7SfwXB-jcv4RYVi6j25qgow9Ew52ufurPqaK-3WAKG32KpV8J4-Wejq8t0c-yA7sb8EnB551b7TU93uKRiVVK3E55Nk5ADPoam_WYE45i3s5qVAP_-InW75NUoOCGTsS8YWMfb6ecHYJ-1j-bzA27zaT9VjctXn9byNFZmEXAMPLExw",
"host":"example1234567890000.appsync-api.us-east-1.amazonaws.com"
```

### IAM
<a name="iam"></a>

#### IAM 标头
<a name="iam-list"></a>

**标头内容**
+  `"accept": "application/json, text/javascript"`：常量 `<string>` 参数。
+  `"content-encoding": "amz-1.0"`：常量 `<string>` 参数。
+  `"content-type": "application/json; charset=UTF-8"`：常量 `<string>` 参数。
+  `"host": <string>`：这是 AWS AppSync GraphQL 端点的主机。
  + `"x-amz-date": <string>`：时间戳必须以 UTC 表示并采用以下 ISO 8601 格式：YYYYMMDD'T'HHMMSS'Z'。例如，20150830T123600Z 是一个有效的时间戳。请勿在时间戳中包含毫秒。有关更多信息，请参阅《*AWS 一般参考*》中的[处理签名版本 4 的日期](https://docs.aws.amazon.com/general/latest/gr/sigv4-date-handling.html)。
  +  `"X-Amz-Security-Token": <string>`：会 AWS 话令牌，使用临时安全证书时是必需的。有关更多信息，请参阅《IAM 用户指南》**中的[将临时凭证用于 AWS 资源](https://docs.aws.amazon.com//IAM/latest/UserGuide/id_credentials_temp_use-resources.html)。
  +  `"Authorization": <string>`: 终端节点的签名版本 4 (Sigv4) 签名信息。 AWS AppSync 有关签名过程的更多信息，请参阅《*AWS 一般参考*》中的[任务 4：将签名添加到 HTTP 请求](https://docs.aws.amazon.com/general/latest/gr/sigv4-add-signature-to-request.html)。

Sigv4 签名 HTTP 请求包含一个规范 URL，它是附加了 `/connect` 的 AWS AppSync GraphQL 终端节点。服务终端节点 AWS 区域与您使用 AWS AppSync API 的区域相同，服务名称为 “appsync”。要签名的 HTTP 请求如下所示：

```
{
  url: "https://example1234567890000.appsync-api.us-east-1.amazonaws.com/graphql/connect",
  data: "{}",
  method: "POST",
  headers: {
    "accept": "application/json, text/javascript",
    "content-encoding": "amz-1.0",
    "content-type": "application/json; charset=UTF-8",
  }
}
```

**示例**

```
{
  "accept": "application/json, text/javascript",
  "content-encoding": "amz-1.0",
  "content-type": "application/json; charset=UTF-8",
  "host": "example1234567890000.appsync-api.us-east-1.amazonaws.com",
  "x-amz-date": "20200401T001010Z",
  "X-Amz-Security-Token": "AgEXAMPLEZ2luX2VjEAoaDmFwLXNvdXRoZWFEXAMPLEcwRQIgAh97Cljq7wOPL8KsxP3YtDuyc/9hAj8PhJ7Fvf38SgoCIQDhJEXAMPLEPspioOztj++pEagWCveZUjKEn0zyUhBEXAMPLEjj//////////8BEXAMPLExODk2NDgyNzg1NSIMo1mWnpESWUoYw4BkKqEFSrm3DXuL8w+ZbVc4JKjDP4vUCKNR6Le9C9pZp9PsW0NoFy3vLBUdAXEXAMPLEOVG8feXfiEEA+1khgFK/wEtwR+9zF7NaMMMse07wN2gG2tH0eKMEXAMPLEQX+sMbytQo8iepP9PZOzlZsSFb/dP5Q8hk6YEXAMPLEYcKZsTkDAq2uKFQ8mYUVA9EtQnNRiFLEY83aKvG/tqLWNnGlSNVx7SMcfovkFDqQamm+88y1OwwAEYK7qcoceX6Z7GGcaYuIfGpaX2MCCELeQvZ+8WxEgOnIfz7GYvsYNjLZSaRnV4G+ILY1F0QNW64S9Nvj+BwDg3ht2CrNvpwjVYlj9U3nmxE0UG5ne83LL5hhqMpm25kmL7enVgw2kQzmU2id4IKu0C/WaoDRuO2F5zE63vJbxN8AYs7338+4B4HBb6BZ6OUgg96Q15RA41/gIqxaVPxyTpDfTU5GfSLxocdYeniqqpFMtZG2n9d0u7GsQNcFkNcG3qDZm4tDo8tZbuym0a2VcF2E5hFEgXBa+XLJCfXi/77OqAEjP0x7Qdk3B43p8KG/BaioP5RsV8zBGvH1zAgyPha2rN70/tT13yrmPd5QYEfwzexjKrV4mWIuRg8NTHYSZJUaeyCwTom80VFUJXG+GYTUyv5W22aBcnoRGiCiKEYTLOkgXecdKFTHmcIAejQ9Welr0a196Kq87w5KNMCkcCGFnwBNFLmfnbpNqT6rUBxxs3X5ntX9d8HVtSYINTsGXXMZCJ7fnbWajhg/aox0FtHX21eF6qIGT8j1z+l2opU+ggwUgkhUUgCH2TfqBj+MLMVVvpgqJsPKt582caFKArIFIvO+9QupxLnEH2hz04TMTfnU6bQC6z1buVe7h+tOLnh1YPFsLQ88anib/7TTC8k9DsBTq0ASe8R2GbSEsmO9qbbMwgEaYUhOKtGeyQsSJdhSk6XxXThrWL9EnwBCXDkICMqdntAxyyM9nWsZ4bL9JHqExgWUmfWChzPFAqn3F4y896UqHTZxlq3WGypn5HHcem2Hqf3IVxKH1inhqdVtkryEiTWrI7ZdjbqnqRbl+WgtPtKOOweDlCaRs3R2qXcbNgVhleMk4IWnF8D1695AenU1LwHjOJLkCjxgNFiWAFEPH9aEXAMPLExA==",
  "Authorization": "AWS4-HMAC-SHA256 Credential=XXXXXXXXXXXXXXXXXXX/20200401/us-east-1/appsync/aws4_request, SignedHeaders=accept;content-encoding;content-type;host;x-amz-date;x-amz-security-token, Signature=83EXAMPLEbcc1fe3ee69f75cd5ebbf4cb4f150e4f99cec869f149c5EXAMPLEdc"
}
```

**通过查询字符串提供标头**

首先，将包含`host`（AWS AppSync GraphQL 端点）和其他授权标头的 JSON 对象转换为字符串。接下来，使用 base64 编码对该字符串进行编码。生成的 base64 编码字符串将作为名为的查询参数添加到 WebSocket URL 中。`header`生成的请求 URL 采用以下形式：

```
wss://example1234567890000.appsync-realtime-api.us-east-1.amazonaws.com/graphql?header=eyJBdXRob3JpemF0aW9uIjoiZXlKcmFXUWlPaUpqYkc1eGIzQTVlVzVNSzA5UVlYSXJNVEpIV0VGTFNYQmllVTVXTkhoc1FqaFBWVzlZTW5NMldsZHZQU0lzSW1Gc1p5STZJbEpUTWpVMkluMC5leUp6ZFdJaU9pSmhObU5tTWpjd055MHhOamd4TFRRMU5ESXRPV1l4T0MxbE5qWTBNVGcyTmpsa016WWlMQ0psZG1WdWRGOXBaQ0k2SW1Wa016TTVNbU5rTFdOallUTXROR00yT0MxaE5EWXlMVEpsWkdJM1pUTm1ZMkZqWmlJc0luUnZhMlZ1WDNWelpTSTZJbUZqWTJWemN5SXNJbk5qYjNCbElqb2lZWGR6TG1OdloyNXBkRzh1YzJsbmJtbHVMblZ6WlhJdVlXUnRhVzRpTENKaGRYUm9YM1JwYldVaU9qRTFOamswTlRjM01UZ3NJbWx6Y3lJNkltaDBkSEJ6T2x3dlhDOWpiMmR1YVhSdkxXbGtjQzVoY0MxemIzVjBhR1ZoYzNRdE1pNWhiV0Y2YjI1aGQzTXVZMjl0WEM5aGNDMXpiM1YwYUdWaGMzUXRNbDgzT0hZMFNWWmliVkFpTENKbGVIQWlPakUxTmprME5qRXpNakFzSW1saGRDSTZNVFUyT1RRMU56Y3lNQ3dpYW5ScElqb2lOVGd6WmpobVltTXRNemsyTVMwMFl6QTRMV0poWlRBdFl6UXlZMkl4TVRNNU5EWTVJaXdpWTJ4cFpXNTBYMmxrSWpvaU0zRmxhalZsTVhabU16ZDFOM1JvWld3MGRHOTFkREprTVd3aUxDSjFjMlZ5Ym1GdFpTSTZJbVZzYjNKNllXWmxJbjAuQjRjZEp0aDNLRk5wSjZpa1ZwN2U2RFJlZTk1VjZRaS16RUUyREpIN3NIT2wyenhZaTdmLVNtRUdvaDJBRDhlbXhRUllhakJ5ei1yRTRKaDBRT3ltTjJZcy1aSWtNcFZCVFBndS1UTVdEeU9IaERVbVVqMk9QODJ5ZVozd2xaQXRyX2dNNEx6alhVWG1JX0syeUdqdVhmWFRhYTFtdlFFQkcwbVFmVmQ3U2Z3WEItamN2NFJZVmk2ajI1cWdvdzlFdzUydWZ1clBxYUstM1dBS0czMktwVjhKNC1XZWpxOHQwYy15QTdzYjhFbkI1NTFiN1RVOTN1S1JpVlZLM0U1NU5rNUFEUG9hbV9XWUU0NWkzczVxVkFQXy1Jblc3NU5Vb09DR1RzUzhZV01mYjZlY0hZSi0xai1iekEyN3phVDlWamN0WG45YnlORlptS0xwQTJMY3h3IiwiaG9zdCI6ImV4YW1wbGUxMjM0NTY3ODkwMDAwLmFwcHN5bmMtYXBpLnVzLWVhc3QtMS5hbWF6b25hd3MuY29tIn0=&payload=e30=
```

值得注意的是，除了 base64 编码的标头对象外，一个空的 JSON 对象 \$1\$1 也经过了 base64 编码，并作为单独的查询参数包含在网址中命名。`payload` WebSocket

**通过 `Sec-WebSocket-Protocol` 提供标头**

包含 `host` 和其他授权标头的 JSON 对象会转换为字符串，然后使用 base64Url 编码进行编码。生成的 base64Url 编码字符串的前缀为 `header-`。然后，在与 AWS AppSync 实时端点建立 WebSocket 连接时，除了`Sec-WebSocket-Protocol`标头外，还会将`graphql-ws`此前缀字符串用作新的子协议。

生成的请求 URL 采用以下形式：

```
wss://example1234567890000.appsync-realtime-api.us-east-1.amazonaws.com/graphql
```

`Sec-WebSocket-Protocol` 标头包含以下值：

```
"sec-websocket-protocol" : ["graphql-ws", "header-ew0KICAiYWNjZXB0IjogImFwcGxpY2F0aW9uL2pzb24sIHRleHQvamF2YXNjcmlwdCIsDQogICJjb250ZW50LWVuY29kaW5nIjogImFtei0xLjAiLA0KICAiY29udGVudC10eXBlIjogImFwcGxpY2F0aW9uL2pzb247IGNoYXJzZXQ9VVRGLTgiLA0KICAiaG9zdCI6ICJleGFtcGxlMTIzNDU2Nzg5MDAwMC5hcHBzeW5jLWFwaS51cy1lYXN0LTEuYW1hem9uYXdzLmNvbSIsDQogICJ4LWFtei1kYXRlIjogIjIwMjAwNDAxVDAwMTAxMFoiLA0KICAiWC1BbXotU2VjdXJpdHktVG9rZW4iOiAiQWdFWEFNUExFWjJsdVgyVmpFQW9hRG1Gd0xYTnZkWFJvWldGRVhBTVBMRWN3UlFJZ0FoOTdDbGpxN3dPUEw4S3N4UDNZdER1eWMvOWhBajhQaEo3RnZmMzhTZ29DSVFEaEpFWEFNUExFUHNwaW9PenRqKytwRWFnV0N2ZVpVaktFbjB6eVVoQkVYQU1QTEVqai8vLy8vLy8vLy84QkVYQU1QTEV4T0RrMk5EZ3lOemcxTlNJTW8xbVducEVTV1VvWXc0QmtLcUVGU3JtM0RYdUw4dytaYlZjNEpLakRQNHZVQ0tOUjZMZTlDOXBacDlQc1cwTm9GeTN2TEJVZEFYRVhBTVBMRU9WRzhmZVhmaUVFQSsxa2hnRksvd0V0d1IrOXpGN05hTU1Nc2UwN3dOMmdHMnRIMGVLTUVYQU1QTEVRWCtzTWJ5dFFvOGllcFA5UFpPemxac1NGYi9kUDVROGhrNllFWEFNUExFWWNLWnNUa0RBcTJ1S0ZROG1ZVVZBOUV0UW5OUmlGTEVZODNhS3ZHL3RxTFdObkdsU05WeDdTTWNmb3ZrRkRxUWFtbSs4OHkxT3d3QUVZSzdxY29jZVg2WjdHR2NhWXVJZkdwYVgyTUNDRUxlUXZaKzhXeEVnT25JZno3R1l2c1lOakxaU2FSblY0RytJTFkxRjBRTlc2NFM5TnZqK0J3RGczaHQyQ3JOdnB3alZZbGo5VTNubXhFMFVHNW5lODNMTDVoaHFNcG0yNWttTDdlblZndzJrUXptVTJpZDRJS3UwQy9XYW9EUnVPMkY1ekU2M3ZKYnhOOEFZczczMzgrNEI0SEJiNkJaNk9VZ2c5NlExNVJBNDEvZ0lxeGFWUHh5VHBEZlRVNUdmU0x4b2NkWWVuaXFxcEZNdFpHMm45ZDB1N0dzUU5jRmtOY0czcURabTR0RG84dFpidXltMGEyVmNGMkU1aEZFZ1hCYStYTEpDZlhpLzc3T3FBRWpQMHg3UWRrM0I0M3A4S0cvQmFpb1A1UnNWOHpCR3ZIMXpBZ3lQaGEyck43MC90VDEzeXJtUGQ1UVlFZnd6ZXhqS3JWNG1XSXVSZzhOVEhZU1pKVWFleUN3VG9tODBWRlVKWEcrR1lUVXl2NVcyMmFCY25vUkdpQ2lLRVlUTE9rZ1hlY2RLRlRIbWNJQWVqUTlXZWxyMGExOTZLcTg3dzVLTk1Da2NDR0Zud0JORkxtZm5icE5xVDZyVUJ4eHMzWDVudFg5ZDhIVnRTWUlOVHNHWFhNWkNKN2ZuYldhamhnL2FveDBGdEhYMjFlRjZxSUdUOGoxeitsMm9wVStnZ3dVZ2toVVVnQ0gyVGZxQmorTUxNVlZ2cGdxSnNQS3Q1ODJjYUZLQXJJRkl2Tys5UXVweExuRUgyaHowNFRNVGZuVTZiUUM2ejFidVZlN2grdE9MbmgxWVBGc0xRODhhbmliLzdUVEM4azlEc0JUcTBBU2U4UjJHYlNFc21POXFiYk13Z0VhWVVoT0t0R2V5UXNTSmRoU2s2WHhYVGhyV0w5RW53QkNYRGtJQ01xZG50QXh5eU05bldzWjRiTDlKSHFFeGdXVW1mV0NoelBGQXFuM0Y0eTg5NlVxSFRaeGxxM1dHeXBuNUhIY2VtMkhxZjNJVnhLSDFpbmhxZFZ0a3J5RWlUV3JJN1pkamJxbnFSYmwrV2d0UHRLT093ZURsQ2FSczNSMnFYY2JOZ1ZobGVNazRJV25GOEQxNjk1QWVuVTFMd0hqT0pMa0NqeGdORmlXQUZFUEg5YUVYQU1QTEV4QT09IiwNCiAgIkF1dGhvcml6YXRpb24iOiAiQVdTNC1ITUFDLVNIQTI1NiBDcmVkZW50aWFsPVhYWFhYWFhYWFhYWFhYWFhYWFgvMjAyMDA0MDEvdXMtZWFzdC0xL2FwcHN5bmMvYXdzNF9yZXF1ZXN0LCBTaWduZWRIZWFkZXJzPWFjY2VwdDtjb250ZW50LWVuY29kaW5nO2NvbnRlbnQtdHlwZTtob3N0O3gtYW16LWRhdGU7eC1hbXotc2VjdXJpdHktdG9rZW4sIFNpZ25hdHVyZT04M0VYQU1QTEViY2MxZmUzZWU2OWY3NWNkNWViYmY0Y2I0ZjE1MGU0Zjk5Y2VjODY5ZjE0OWM1RVhBTVBMRWRjIg0KfQ"]
```

**通过标准 HTTP 标头提供标头**

在这种方法中，在与 AWS AppSync 实时端点建立 WebSocket 连接时，使用标准 HTTP 标头传输主机和其他授权信息。生成的请求 URL 采用以下形式：

```
wss://example1234567890000.appsync-realtime-api.us-east-1.amazonaws.com/graphql
```

请求标头将包括以下内容：

```
"sec-websocket-protocol" : ["graphql-ws"]
"accept": "application/json, text/javascript",
"content-encoding": "amz-1.0",
"content-type": "application/json; charset=UTF-8",
"host": "example1234567890000.appsync-api.us-east-1.amazonaws.com",
"x-amz-date": "20200401T001010Z",
"X-Amz-Security-Token": "AgEXAMPLEZ2luX2VjEAoaDmFwLXNvdXRoZWFEXAMPLEcwRQIgAh97Cljq7wOPL8KsxP3YtDuyc/9hAj8PhJ7Fvf38SgoCIQDhJEXAMPLEPspioOztj++pEagWCveZUjKEn0zyUhBEXAMPLEjj//////////8BEXAMPLExODk2NDgyNzg1NSIMo1mWnpESWUoYw4BkKqEFSrm3DXuL8w+ZbVc4JKjDP4vUCKNR6Le9C9pZp9PsW0NoFy3vLBUdAXEXAMPLEOVG8feXfiEEA+1khgFK/wEtwR+9zF7NaMMMse07wN2gG2tH0eKMEXAMPLEQX+sMbytQo8iepP9PZOzlZsSFb/dP5Q8hk6YEXAMPLEYcKZsTkDAq2uKFQ8mYUVA9EtQnNRiFLEY83aKvG/tqLWNnGlSNVx7SMcfovkFDqQamm+88y1OwwAEYK7qcoceX6Z7GGcaYuIfGpaX2MCCELeQvZ+8WxEgOnIfz7GYvsYNjLZSaRnV4G+ILY1F0QNW64S9Nvj+BwDg3ht2CrNvpwjVYlj9U3nmxE0UG5ne83LL5hhqMpm25kmL7enVgw2kQzmU2id4IKu0C/WaoDRuO2F5zE63vJbxN8AYs7338+4B4HBb6BZ6OUgg96Q15RA41/gIqxaVPxyTpDfTU5GfSLxocdYeniqqpFMtZG2n9d0u7GsQNcFkNcG3qDZm4tDo8tZbuym0a2VcF2E5hFEgXBa+XLJCfXi/77OqAEjP0x7Qdk3B43p8KG/BaioP5RsV8zBGvH1zAgyPha2rN70/tT13yrmPd5QYEfwzexjKrV4mWIuRg8NTHYSZJUaeyCwTom80VFUJXG+GYTUyv5W22aBcnoRGiCiKEYTLOkgXecdKFTHmcIAejQ9Welr0a196Kq87w5KNMCkcCGFnwBNFLmfnbpNqT6rUBxxs3X5ntX9d8HVtSYINTsGXXMZCJ7fnbWajhg/aox0FtHX21eF6qIGT8j1z+l2opU+ggwUgkhUUgCH2TfqBj+MLMVVvpgqJsPKt582caFKArIFIvO+9QupxLnEH2hz04TMTfnU6bQC6z1buVe7h+tOLnh1YPFsLQ88anib/7TTC8k9DsBTq0ASe8R2GbSEsmO9qbbMwgEaYUhOKtGeyQsSJdhSk6XxXThrWL9EnwBCXDkICMqdntAxyyM9nWsZ4bL9JHqExgWUmfWChzPFAqn3F4y896UqHTZxlq3WGypn5HHcem2Hqf3IVxKH1inhqdVtkryEiTWrI7ZdjbqnqRbl+WgtPtKOOweDlCaRs3R2qXcbNgVhleMk4IWnF8D1695AenU1LwHjOJLkCjxgNFiWAFEPH9aEXAMPLExA==",
"Authorization": "AWS4-HMAC-SHA256 Credential=XXXXXXXXXXXXXXXXXXX/20200401/us-east-1/appsync/aws4_request, SignedHeaders=accept;content-encoding;content-type;host;x-amz-date;x-amz-security-token, Signature=83EXAMPLEbcc1fe3ee69f75cd5ebbf4cb4f150e4f99cec869f149c5EXAMPLEdc"
```

使用自定义域对请求进行签名：

```
{
  url: "https://api.example.com/graphql/connect",
  data: "{}",
  method: "POST",
  headers: {
    "accept": "application/json, text/javascript",
    "content-encoding": "amz-1.0",
    "content-type": "application/json; charset=UTF-8",
  }
}
```

**示例**

```
{
  "accept": "application/json, text/javascript",
  "content-encoding": "amz-1.0",
  "content-type": "application/json; charset=UTF-8",
  "host": "api.example.com",
  "x-amz-date": "20200401T001010Z",
  "X-Amz-Security-Token": "AgEXAMPLEZ2luX2VjEAoaDmFwLXNvdXRoZWFEXAMPLEcwRQIgAh97Cljq7wOPL8KsxP3YtDuyc/9hAj8PhJ7Fvf38SgoCIQDhJEXAMPLEPspioOztj++pEagWCveZUjKEn0zyUhBEXAMPLEjj//////////8BEXAMPLExODk2NDgyNzg1NSIMo1mWnpESWUoYw4BkKqEFSrm3DXuL8w+ZbVc4JKjDP4vUCKNR6Le9C9pZp9PsW0NoFy3vLBUdAXEXAMPLEOVG8feXfiEEA+1khgFK/wEtwR+9zF7NaMMMse07wN2gG2tH0eKMEXAMPLEQX+sMbytQo8iepP9PZOzlZsSFb/dP5Q8hk6YEXAMPLEYcKZsTkDAq2uKFQ8mYUVA9EtQnNRiFLEY83aKvG/tqLWNnGlSNVx7SMcfovkFDqQamm+88y1OwwAEYK7qcoceX6Z7GGcaYuIfGpaX2MCCELeQvZ+8WxEgOnIfz7GYvsYNjLZSaRnV4G+ILY1F0QNW64S9Nvj+BwDg3ht2CrNvpwjVYlj9U3nmxE0UG5ne83LL5hhqMpm25kmL7enVgw2kQzmU2id4IKu0C/WaoDRuO2F5zE63vJbxN8AYs7338+4B4HBb6BZ6OUgg96Q15RA41/gIqxaVPxyTpDfTU5GfSLxocdYeniqqpFMtZG2n9d0u7GsQNcFkNcG3qDZm4tDo8tZbuym0a2VcF2E5hFEgXBa+XLJCfXi/77OqAEjP0x7Qdk3B43p8KG/BaioP5RsV8zBGvH1zAgyPha2rN70/tT13yrmPd5QYEfwzexjKrV4mWIuRg8NTHYSZJUaeyCwTom80VFUJXG+GYTUyv5W22aBcnoRGiCiKEYTLOkgXecdKFTHmcIAejQ9Welr0a196Kq87w5KNMCkcCGFnwBNFLmfnbpNqT6rUBxxs3X5ntX9d8HVtSYINTsGXXMZCJ7fnbWajhg/aox0FtHX21eF6qIGT8j1z+l2opU+ggwUgkhUUgCH2TfqBj+MLMVVvpgqJsPKt582caFKArIFIvO+9QupxLnEH2hz04TMTfnU6bQC6z1buVe7h+tOLnh1YPFsLQ88anib/7TTC8k9DsBTq0ASe8R2GbSEsmO9qbbMwgEaYUhOKtGeyQsSJdhSk6XxXThrWL9EnwBCXDkICMqdntAxyyM9nWsZ4bL9JHqExgWUmfWChzPFAqn3F4y896UqHTZxlq3WGypn5HHcem2Hqf3IVxKH1inhqdVtkryEiTWrI7ZdjbqnqRbl+WgtPtKOOweDlCaRs3R2qXcbNgVhleMk4IWnF8D1695AenU1LwHjOJLkCjxgNFiWAFEPH9aEXAMPLExA==",
  "Authorization": "AWS4-HMAC-SHA256 Credential=XXXXXXXXXXXXXXXXXXX/20200401/us-east-1/appsync/aws4_request, SignedHeaders=accept;content-encoding;content-type;host;x-amz-date;x-amz-security-token, Signature=83EXAMPLEbcc1fe3ee69f75cd5ebbf4cb4f150e4f99cec869f149c5EXAMPLEdc"
}
```

**带查询字符串的请求 URL**

```
wss://api.example.com/graphql?header=eyEXAMPLEHQiOiJhcHBsaWNhdGlvbi9qc29uLCB0ZXh0L2phdmFEXAMPLEQiLCJjb250ZW50LWVuY29kaW5nIjoEXAMPLEEuMCIsImNvbnRlbnQtdHlwZSI6ImFwcGxpY2F0aW9EXAMPLE47IGNoYXJzZXQ9VVRGLTgiLCJob3N0IjoiZXhhbXBsZEXAMPLENjc4OTAwMDAuYXBwc3luYy1hcGkudXMtZWFzdC0xLmFtYEXAMPLEcy5jb20iLCJ4LWFtei1kYXRlIjoiMjAyMDA0MDFUMDAxMDEwWiIsIlgtEXAMPLElY3VyaXR5LVRva2VuIjoiQWdvSmIzSnBaMmx1WDJWakVBb2FEbUZ3TFhOdmRYUm9aV0Z6ZEMweUlrY3dSUUlnQWg5N0NsanE3d09QTDhLc3hQM1l0RHV5Yy85aEFqOFBoSjdGdmYzOFNnb0NJUURoSllKYkpsbmpQc3Bpb096dGorK3BFYWdXQ3ZlWlVqS0VuMHp5VWhCbXhpck5CUWpqLy8vLy8vLy8vLzhCRUFBYUREY3hPRGsyTkRneU56ZzFOU0lNbzFtV25wRVNXVW9ZdzRCa0txRUZTcm0zRFh1TDh3K1piVmM0SktqRFA0dlVDS05SNkxlOUM5cFpwOVBzVzBOb0Z5M3ZMQlVkQVh3dDZQSld1T1ZHOGZlWGZpRUVBKzFraGdGSy93RXR3Uis5ekY3TmFNTU1zZTA3d04yZ0cydEgwZUtNVFhuOEF3QVFYK3NNYnl0UW84aWVwUDlQWk96bFpzU0ZiL2RQNVE4aGs2WWpHVGFMMWVZY0tac1RrREFxMnVLRlE4bVlVVkE5RXRRbk5SaUZMRVk4M2FLdkcvdHFMV05uR2xTTlZ4N1NNY2ZvdmtGRHFRYW1tKzg4eTFPd3dBRVlLN3Fjb2NlWDZaN0dHY2FZdUlmR3BhWDJNQ0NFTGVRdlorOFd4RWdPbklmejdHWXZzWU5qTFpTYVJuVjRHK0lMWTFGMFFOVzY0UzlOdmorQndEZzNodDJDck52cHdqVllsajlVM25teEUwVUc1bmU4M0xMNWhocU1wbTI1a21MN2VuVmd3MmtRem1VMmlkNElLdTBDL1dhb0RSdU8yRjV6RTYzdkpieE44QVlzNzMzOCs0QjRIQmI2Qlo2T1VnZzk2UTE1UkE0MS9nSXF4YVZQeHlUcERmVFU1R2ZTTHhvY2RZZW5pcXFwRk10WkcybjlkMHU3R3NRTmNGa05jRzNxRFptNHREbzh0WmJ1eW0wYTJWY0YyRTVoRkVnWEJhK1hMSkNmWGkvNzdPcUFFalAweDdRZGszQjQzcDhLRy9CYWlvUDVSc1Y4ekJHdkgxekFneVBoYTJyTjcwL3RUMTN5cm1QZDVRWUVmd3pleGpLclY0bVdJdVJnOE5USFlTWkpVYWV5Q3dUb204MFZGVUpYRytHWVRVeXY1VzIyYUJjbm9SR2lDaUtFWVRMT2tnWGVjZEtGVEhtY0lBZWpROVdlbHIwYTE5NktxODd3NUtOTUNrY0NHRm53Qk5GTG1mbmJwTnFUNnJVQnh4czNYNW50WDlkOEhWdFNZSU5Uc0dYWE1aQ0o3Zm5iV2FqaGcvYW94MEZ0SFgyMWVGNnFJR1Q4ajF6K2wyb3BVK2dnd1Vna2hVVWdDSDJUZnFCaitNTE1WVnZwZ3FKc1BLdDU4MmNhRktBcklGSXZPKzlRdXB4TG5FSDJoejA0VE1UZm5VNmJRQzZ6MWJ1VmU3aCt0T0xuaDFZUEZzTFE4OGFuaWIvN1RUQzhrOURzQlRxMEFTZThSMkdiU0VzbU85cWJiTXdnRWFZVWhPS3RHZXlRc1NKZGhTazZYeFhUaHJXTDlFbndCQ1hEa0lDTXFkbnRBeHl5TTluV3NaNGJMOUpIcUV4Z1dVbWZXQ2h6UEZBcW4zRjR5ODk2VXFIVFp4bHEzV0d5cG41SEhjZW0ySHFmM0lWeEtIMWluaHFkVnRrcnlFaVRXckk3WmRqYnFucVJibCtXZ3RQdEtPT3dlRGxDYVJzM1IycVhjYk5nVmhsZU1rNElXbkY4RDE2OTVBZW5VMUx3SGpPSkxrQ2p4Z05GaVdBRkVQSDlhTklhcXMvWnhBPT0iLCJBdXRob3JpemF0aW9uIjoiQVdTNC1ITUFDLVNIQTI1NiBDcmVkZW50aWFsPVhYWFhYWFhYWFhYWFhYWFhYWFgvMjAxOTEwMDIvdXMtZWFzdC0xEXAMPLE5bmMvYXdzNF9yZXF1ZXN0LCBTaWduZWRIZWFkZXJzPWFjY2VwdDtjb250ZWEXAMPLE29kaW5nO2NvbnRlbnQtdHlwZTtob3EXAMPLEW16LWRhdGU7eC1hbXotc2VjdXJpdHktdG9rZW4sIFNpZ25hdHVyZT04MzE4EXAMPLEiY2MxZmUzZWU2OWY3NWNkEXAMPLE0Y2I0ZjE1MGU0Zjk5Y2VjODY5ZjE0OWM1ZDAzNDEXAMPLEn0=&payload=e30=
```

**注意**  
一个 WebSocket 连接可以有多个订阅（即使使用不同的身份验证模式也是如此）。实现这一点的一种方法是为第一个订阅创建一个 WebSocket 连接，然后在最后一个订阅取消注册时将其关闭。您可以通过等待几秒钟再关闭 WebSocket 连接来进行优化，以防应用程序在最后一次订阅取消注册后立即订阅。以移动应用程序为例，从一个屏幕切换到另一个屏幕时，它在发生*卸载*事件时停止订阅，并在发生*挂载*事件时启动另一个订阅。

### Lambda 授权
<a name="lambda-auth"></a>

#### Lambda 授权标头
<a name="lambda-auth-list"></a>

**标头内容**
+  `"Authorization": <string>`：作为 `authorizationToken` 传递的值。
+  `"host": <string>`： AWS AppSyncGraphQL 终端节点的主机或您的自定义域名。

**示例**

```
{
    "Authorization":"M0UzQzM1MkQtMkI0Ni00OTZCLUI1NkQtMUM0MTQ0QjVBRTczCkI1REEzRTIxLTk5NzItNDJENi1BQjMwLTFCNjRFNzQ2NzlCNQo=",
    "host":"example1234567890000.appsync-api.us-east-1.amazonaws.com"
}
```

**通过查询字符串提供标头**

首先，将包含 `host` 和 `Authorization` 的 JSON 对象转换为字符串。接下来，使用 base64 编码对该字符串进行编码。生成的 base64 编码字符串作为名为的查询参数添加`header`到 WebSocket URL 中，用于与实时端点建立连接。 AWS AppSync生成的请求 URL 采用以下形式：

```
wss://example1234567890000.appsync-realtime-api.us-east-1.amazonaws.com/graphql?header=eyJBdXRob3JpemF0aW9uIjoiZXlKcmFXUWlPaUpqYkc1eGIzQTVlVzVNSzA5UVlYSXJNVEpIV0VGTFNYQmllVTVXTkhoc1FqaFBWVzlZTW5NMldsZHZQU0lzSW1Gc1p5STZJbEpUTWpVMkluMC5leUp6ZFdJaU9pSmhObU5tTWpjd055MHhOamd4TFRRMU5ESXRPV1l4T0MxbE5qWTBNVGcyTmpsa016WWlMQ0psZG1WdWRGOXBaQ0k2SW1Wa016TTVNbU5rTFdOallUTXROR00yT0MxaE5EWXlMVEpsWkdJM1pUTm1ZMkZqWmlJc0luUnZhMlZ1WDNWelpTSTZJbUZqWTJWemN5SXNJbk5qYjNCbElqb2lZWGR6TG1OdloyNXBkRzh1YzJsbmJtbHVMblZ6WlhJdVlXUnRhVzRpTENKaGRYUm9YM1JwYldVaU9qRTFOamswTlRjM01UZ3NJbWx6Y3lJNkltaDBkSEJ6T2x3dlhDOWpiMmR1YVhSdkxXbGtjQzVoY0MxemIzVjBhR1ZoYzNRdE1pNWhiV0Y2YjI1aGQzTXVZMjl0WEM5aGNDMXpiM1YwYUdWaGMzUXRNbDgzT0hZMFNWWmliVkFpTENKbGVIQWlPakUxTmprME5qRXpNakFzSW1saGRDSTZNVFUyT1RRMU56Y3lNQ3dpYW5ScElqb2lOVGd6WmpobVltTXRNemsyTVMwMFl6QTRMV0poWlRBdFl6UXlZMkl4TVRNNU5EWTVJaXdpWTJ4cFpXNTBYMmxrSWpvaU0zRmxhalZsTVhabU16ZDFOM1JvWld3MGRHOTFkREprTVd3aUxDSjFjMlZ5Ym1GdFpTSTZJbVZzYjNKNllXWmxJbjAuQjRjZEp0aDNLRk5wSjZpa1ZwN2U2RFJlZTk1VjZRaS16RUUyREpIN3NIT2wyenhZaTdmLVNtRUdvaDJBRDhlbXhRUllhakJ5ei1yRTRKaDBRT3ltTjJZcy1aSWtNcFZCVFBndS1UTVdEeU9IaERVbVVqMk9QODJ5ZVozd2xaQXRyX2dNNEx6alhVWG1JX0syeUdqdVhmWFRhYTFtdlFFQkcwbVFmVmQ3U2Z3WEItamN2NFJZVmk2ajI1cWdvdzlFdzUydWZ1clBxYUstM1dBS0czMktwVjhKNC1XZWpxOHQwYy15QTdzYjhFbkI1NTFiN1RVOTN1S1JpVlZLM0U1NU5rNUFEUG9hbV9XWUU0NWkzczVxVkFQXy1Jblc3NU5Vb09DR1RzUzhZV01mYjZlY0hZSi0xai1iekEyN3phVDlWamN0WG45YnlORlptS0xwQTJMY3h3IiwiaG9zdCI6ImV4YW1wbGUxMjM0NTY3ODkwMDAwLmFwcHN5bmMtYXBpLnVzLWVhc3QtMS5hbWF6b25hd3MuY29tIn0=&payload=e30=
```

值得注意的是，除了 base64 编码的标头对象外，一个空的 JSON 对象 \$1\$1 也经过了 base64 编码，并作为单独的查询参数包含在网址中命名。`payload` WebSocket

**通过 `Sec-WebSocket-Protocol` 提供标头**

包含 `host` 和 `Authorization` 的 JSON 对象会转换为字符串，然后使用 base64Url 编码进行编码。生成的 base64Url 编码字符串的前缀为 `header-`。然后，在与 AWS AppSync 实时端点建立 WebSocket 连接时，除了`Sec-WebSocket-Protocol`标头外，还会将`graphql-ws`此前缀字符串用作新的子协议。

生成的请求 URL 采用以下形式：

```
wss://example1234567890000.appsync-realtime-api.us-east-1.amazonaws.com/graphql
```

`Sec-WebSocket-Protocol` 标头包含以下值：

```
"sec-websocket-protocol" : ["graphql-ws", "header-ewogICAgImhvc3QiOiJleGFtcGxlMTIzNDU2Nzg5MDAwMC5hcHBzeW5jLWFwaS51cy1lYXN0LTEuYW1hem9uYXdzLmNvbSIsCiAgICAieC1hcGkta2V5IjoiZGEyLTEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Igp9"]
```

**通过标准 HTTP 标头提供标头**

在这种方法中，在与 AWS AppSync 实时端点建立 WebSocket 连接时，使用标准 HTTP 标头传输主机和授权信息。生成的请求 URL 采用以下形式：

```
wss://example1234567890000.appsync-realtime-api.us-east-1.amazonaws.com/graphql
```

请求标头将包括以下内容：

```
"sec-websocket-protocol" : ["graphql-ws"]
"Authorization":"eyEXAMPLEiJjbG5xb3A5eW5MK09QYXIrMTJHWEFLSXBieU5WNHhsQjEXAMPLEnM2WldvPSIsImFsZyI6IlEXAMPLEn0.eyEXAMPLEiJhNmNmMjcwNy0xNjgxLTQ1NDItOWYxOC1lNjY0MTg2NjlkMzYiLCJldmVudF9pZCI6ImVkMzM5MmNkLWNjYTMtNGM2OC1hNDYyLTJlZGI3ZTNmY2FjZiIsInRva2VuX3VzZSI6ImFjY2VzcyIsInNjb3BlIjoiYXdzLmNvZ25pdG8uc2lnbmluLnVzZXIuYWRtaW4iLCJhdXRoX3RpbWUiOjE1Njk0NTc3MTgsImlzcyI6Imh0dHBzOlwvXC9jb2duaXRvLWlkcC5hcC1zb3V0aGVhc3QtMi5hbWF6b25hd3MuY29tXC9hcC1zb3V0aGVhc3QtMl83OHY0SVZibVAiLCJleHAiOjE1Njk0NjEzMjAsImlhdCI6MTU2OTQ1NzcyMCwianRpIjoiNTgzZjhmYmMtMzk2MS00YzA4LWJhZTAtYzQyY2IxMTM5NDY5IiwiY2xpZW50X2lkIjoiM3FlajVlMXZmMzd1N3RoZWw0dG91dDJkMWwiLCJ1c2VybmFtZSI6ImVsb3EXAMPLEn0.B4EXAMPLEFNpJ6ikVp7e6DRee95V6Qi-zEE2DJH7sHOl2zxYi7f-SmEGoh2AD8emxQRYajByz-rE4Jh0QOymN2Ys-ZIkMpVBTPgu-TMWDyOHhDUmUj2OP82yeZ3wlZAtr_gM4LzjXUXmI_K2yGjuXfXTaa1mvQEBG0mQfVd7SfwXB-jcv4RYVi6j25qgow9Ew52ufurPqaK-3WAKG32KpV8J4-Wejq8t0c-yA7sb8EnB551b7TU93uKRiVVK3E55Nk5ADPoam_WYE45i3s5qVAP_-InW75NUoOCGTsS8YWMfb6ecHYJ-1j-bzA27zaT9VjctXn9byNFZmEXAMPLExw",
"host":"example1234567890000.appsync-api.us-east-1.amazonaws.com"
```

## 实时 WebSocket 操作
<a name="real-time-websocket-operation"></a>

成功启动与的 WebSocket 握手后 AWS AppSync，客户端必须发送后续消息以 AWS AppSync 进行不同的操作。这些消息需要以下数据：
+  `type`：操作的类型。
+  `id`：订阅的唯一标识符。我们建议为此目的使用 UUID。
+  `payload`：关联的负载，取决于操作类型。

`type` 字段是唯一的必填字段；`id` 和 `payload` 字段是可选的。

### 事件序列
<a name="sequence-of-events"></a>

要成功启动、建立、注册和处理订阅请求，客户端必须执行以下序列：

1. 初始化连接 (`connection_init`)

1. 连接确认 (`connection_ack`)

1. 订阅注册 (`start`)

1. 订阅确认 (`start_ack`)

1. 处理订阅 (`data`)

1. 订阅取消注册 (`stop`)

## 连接启动消息
<a name="connection-init-message"></a>

（可选）成功握手后，客户端可以发送`connection_init`消息开始与 AWS AppSync实时端点通信。该消息是通过将 JSON 对象字符串化获得的字符串，如下所示：

```
{ "type": "connection_init" }
```

## 连接确认消息
<a name="connection-acknowledge-message"></a>

发送 `connection_init` 消息后，客户端必须等待 `connection_ack` 消息。将忽略在收到 `connection_ack` 之前发送的所有消息。该消息具体如下：

```
{
  "type": "connection_ack",
  "payload": {
    // Time in milliseconds waiting for ka message before the client should terminate the WebSocket connection
    "connectionTimeoutMs": 300000
  }
}
```

## 保持活动消息
<a name="keep-alive-message"></a>

除了连接确认消息以外，客户端还会定期收到保持活动消息。如果客户端在连接超时时间内没有收到保持活动状态的消息，则客户端应关闭连接。 AWS AppSync 继续发送这些消息并为注册的订阅提供服务，直到它自动关闭连接（24 小时后）。保持活动消息是检测信号，客户端不需要确认这些消息。

```
{ "type": "ka" }
```

## 订阅注册消息
<a name="subscription-registration-message"></a>

客户端收到`connection_ack`消息后，可以向发送订阅注册消息 AWS AppSync。这种类型的消息是字符串化 JSON 对象，其中包含以下字段：
+  `"id": <string>`：订阅的 ID。对于每个订阅，该 ID 必须是唯一的，否则，服务器返回错误，以指示订阅 ID 是重复的。
+  `"type": "start"`：常量 `<string>` 参数。
+  `"payload": <Object>`：包含与订阅相关的信息的对象。
  +  `"data": <string>`：包含 GraphQL 查询和变量的字符串化 JSON 对象。
    +  `"query": <string>`：GraphQL 操作。
    +  `"variables": <Object>`：包含查询变量的对象。
  +  `"extensions": <Object>`：包含授权对象的对象。
+  `"authorization": <Object>`：包含授权所需的字段的对象。

### 订阅注册的授权对象
<a name="authorization-object-for-subscription-registration"></a>

[基于 AWS AppSync API 授权模式的标头参数格式](#header-parameter-format-based-on-appsync-api-authorization-mode)一节中的相同规则适用于授权对象。唯一的例外是 IAM，其中的 SigV4 签名信息略有不同。有关详细信息，请参阅 IAM 示例。

使用 Amazon Cognito 用户池的示例：

```
{
  "id": "ee849ef0-cf23-4cb8-9fcb-152ae4fd1e69",
  "payload": {
    "data": "{\"query\":\"subscription onCreateMessage {\\n onCreateMessage {\\n __typename\\n message\\n }\\n }\",\"variables\":{}}",
      "extensions": {
        "authorization": {
          "Authorization": "eyEXAMPLEiJjbG5xb3A5eW5MK09QYXIrMTJEXAMPLEBieU5WNHhsQjhPVW9YMnM2WldvPSIsImFsZyI6IlEXAMPLEn0.eyJzdWIiOiJhNmNmMjcwNy0xNjgxLTQ1NDItEXAMPLENjY0MTg2NjlkMzYiLCJldmVudF9pZCI6ImU3YWVmMzEyLWUEXAMPLEY0Zi04YjlhLTRjMWY5M2Q5ZTQ2OCIsInRva2VuX3VzZSI6ImFjY2VzcyIsIEXAMPLEIjoiYXdzLmNvZ25pdG8uc2lnbmluLnVzZXIuYWRtaW4iLCJhdXRoX3RpbWUiOjE1Njk2MTgzMzgsImlzcyI6Imh0dEXAMPLEXC9jb2duaXRvLWlkcC5hcC1zb3V0aGVhc3QtMi5hbWF6b25hd3MuY29tXC9hcC1zbEXAMPLEc3QtMl83OHY0SVZibVAiLCJleHAiOjE1NzAyNTQ3NTUsImlhdCI6MTU3MDI1MTE1NSwianRpIjoiMmIEXAMPLEktZTVkMi00ZDhkLWJiYjItNjA0YWI4MDEwOTg3IiwiY2xpZW50X2lkIjoiM3FlajVlMXZmMzd1EXAMPLE0dG91dDJkMWwiLCJ1c2VybmFtZSI6ImVsb3J6YWZlIn0.CT-qTCtrYeboUJ4luRSTPXaNewNeEXAMPLE14C6sfg05tO0fOMpiUwj9k19gtNCCMqoSsjtQoUweFnH4JYa5EXAMPLEVxOyQEQ4G7jQrt5Ks6STn53vuseR3zRW9snWgwz7t3ZmQU-RWvW7yQU3sNQRLEXAMPLEcd0yufBiCYs3dfQxTTdvR1B6Wz6CD78lfNeKqfzzUn2beMoup2h6EXAMPLE4ow8cUPUPvG0DzRtHNMbWskjPanu7OuoZ8iFO_Eot9kTtAlVKYoNbWkZhkD8dxutyoU4RSH5JoLAnrGF5c8iKgv0B2dfEXAMPLEIihxaZVJ9w9w48S4EXAMPLEcA",
          "host": "example1234567890000.appsync-api.us-east-1.amazonaws.com"
         }
      }
  },
  "type": "start"
}
```

使用 IAM 的示例：

```
{
  "id": "eEXAMPLE-cf23-1234-5678-152EXAMPLE69",
  "payload": {
    "data": "{\"query\":\"subscription onCreateMessage {\\n onCreateMessage {\\n __typename\\n message\\n }\\n }\",\"variables\":{}}",
    "extensions": {
      "authorization": {
        "accept": "application/json, text/javascript",
        "content-type": "application/json; charset=UTF-8",
        "X-Amz-Security-Token": "AgEXAMPLEZ2luX2VjEAoaDmFwLXNvdXRoZWFEXAMPLEcwRQIgAh97Cljq7wOPL8KsxP3YtDuyc/9hAj8PhJ7Fvf38SgoCIQDhJEXAMPLEPspioOztj++pEagWCveZUjKEn0zyUhBEXAMPLEjj//////////8BEXAMPLExODk2NDgyNzg1NSIMo1mWnpESWUoYw4BkKqEFSrm3DXuL8w+ZbVc4JKjDP4vUCKNR6Le9C9pZp9PsW0NoFy3vLBUdAXEXAMPLEOVG8feXfiEEA+1khgFK/wEtwR+9zF7NaMMMse07wN2gG2tH0eKMEXAMPLEQX+sMbytQo8iepP9PZOzlZsSFb/dP5Q8hk6YEXAMPLEYcKZsTkDAq2uKFQ8mYUVA9EtQnNRiFLEY83aKvG/tqLWNnGlSNVx7SMcfovkFDqQamm+88y1OwwAEYK7qcoceX6Z7GGcaYuIfGpaX2MCCELeQvZ+8WxEgOnIfz7GYvsYNjLZSaRnV4G+ILY1F0QNW64S9Nvj+BwDg3ht2CrNvpwjVYlj9U3nmxE0UG5ne83LL5hhqMpm25kmL7enVgw2kQzmU2id4IKu0C/WaoDRuO2F5zE63vJbxN8AYs7338+4B4HBb6BZ6OUgg96Q15RA41/gIqxaVPxyTpDfTU5GfSLxocdYeniqqpFMtZG2n9d0u7GsQNcFkNcG3qDZm4tDo8tZbuym0a2VcF2E5hFEgXBa+XLJCfXi/77OqAEjP0x7Qdk3B43p8KG/BaioP5RsV8zBGvH1zAgyPha2rN70/tT13yrmPd5QYEfwzexjKrV4mWIuRg8NTHYSZJUaeyCwTom80VFUJXG+GYTUyv5W22aBcnoRGiCiKEYTLOkgXecdKFTHmcIAejQ9Welr0a196Kq87w5KNMCkcCGFnwBNFLmfnbpNqT6rUBxxs3X5ntX9d8HVtSYINTsGXXMZCJ7fnbWajhg/aox0FtHX21eF6qIGT8j1z+l2opU+ggwUgkhUUgCH2TfqBj+MLMVVvpgqJsPKt582caFKArIFIvO+9QupxLnEH2hz04TMTfnU6bQC6z1buVe7h+tOLnh1YPFsLQ88anib/7TTC8k9DsBTq0ASe8R2GbSEsmO9qbbMwgEaYUhOKtGeyQsSJdhSk6XxXThrWL9EnwBCXDkICMqdntAxyyM9nWsZ4bL9JHqExgWUmfWChzPFAqn3F4y896UqHTZxlq3WGypn5HHcem2Hqf3IVxKH1inhqdVtkryEiTWrI7ZdjbqnqRbl+WgtPtKOOweDlCaRs3R2qXcbNgVhleMk4IWnF8D1695AenU1LwHjOJLkCjxgNFiWAFEPH9aEXAMPLExA==",
        "Authorization": "AWS4-HMAC-SHA256 Credential=XXXXXXXXXXXXXXXXXXXX/20200401/us-east-1/appsync/aws4_request, SignedHeaders=accept;content-encoding;content-type;host;x-amz-date;x-amz-security-token, Signature=b90131a61a7c4318e1c35ead5dbfdeb46339a7585bbdbeceeaff51f4022eb1fd",
        "content-encoding": "amz-1.0",
        "host": "example1234567890000.appsync-api.us-east-1.amazonaws.com",
        "x-amz-date": "20200401T001010Z"
      }
    }
  },
  "type": "start"
}
```

使用自定义域名的示例：

```
{
  "id": "key-cf23-4cb8-9fcb-152ae4fd1e69",
  "payload": {
    "data": "{\"query\":\"subscription onCreateMessage {\\n onCreateMessage {\\n __typename\\n message\\n }\\n }\",\"variables\":{}}",
      "extensions": {
        "authorization": {
          "x-api-key": "da2-12345678901234567890123456",
          "host": "api.example.com"
         }
      }
  },
  "type": "start"
}
```

SigV4 签名不需要将 `/connect` 附加到 URL 中，并使用 JSON 字符串化 GraphQL 操作替换 `data`。以下是一个 SigV4 签名请求示例：

```
{
  url: "https://example1234567890000.appsync-api.us-east-1.amazonaws.com/graphql",
  data: "{\"query\":\"subscription onCreateMessage {\\n onCreateMessage {\\n __typename\\n message\\n }\\n }\",\"variables\":{}}",
  method: "POST",
  headers: {
    "accept": "application/json, text/javascript",
    "content-encoding": "amz-1.0",
    "content-type": "application/json; charset=UTF-8",
  }
}
```

## 订阅确认消息
<a name="subscription-acknowledge-message"></a>

发送订阅开始消息后，客户端应等待 AWS AppSync发送`start_ack`消息。`start_ack` 消息表示订阅成功。

订阅确认示例：

```
{
  "type": "start_ack",
  "id": "eEXAMPLE-cf23-1234-5678-152EXAMPLE69"
}
```

## 错误消息
<a name="error-message"></a>

如果连接启动或订阅注册失败，或者从服务器中终止订阅，服务器将向客户端发送错误消息。如果在连接初始化期间发生错误，则服务器会关闭连接。
+  `"type": "error"`：常量 `<string>` 参数。
+  `"id": <string>`：注册的相应订阅的 ID（如果相关）。
+  `"payload" <Object>`：包含相应错误信息的对象。

示例：

```
{
  "type": "error",
  "payload": {
    "errors": [
      {
        "errorType": "LimitExceededError",
        "message": "Rate limit exceeded"
      }
    ]
  }
}
```

## 处理数据消息
<a name="processing-data-messages"></a>

当客户端提交突变时，会 AWS AppSync 识别所有对此变更感兴趣的订阅者，并使用订阅操作中的相应订阅向每个订阅`id`者`"start"`发送`"type":"data"`消息。客户端应跟踪它发送的订阅 `id`，这样，在它收到数据消息时，客户端可以将其与相应的订阅进行匹配。
+  `"type": "data"`：常量 `<string>` 参数。
+  `"id": <string>`：注册的相应订阅的 ID。
+  `"payload" <Object>`：包含订阅信息的对象。

示例：

```
{
  "type": "data",
  "id": "ee849ef0-cf23-4cb8-9fcb-152ae4fd1e69",
  "payload": {
    "data": {
      "onCreateMessage": {
        "__typename": "Message",
        "message": "test"
      }
    }
  }
}
```

## 订阅取消注册消息
<a name="subscription-unregistration-message"></a>

在应用程序希望停止侦听订阅事件时，客户端应发送包含以下字符串化 JSON 对象的消息：
+  `"type": "stop"`：常量 `<string>` 参数。
+  `"id": <string>`：要取消注册的订阅的 ID。

示例：

```
{
  "type":"stop",
  "id":"ee849ef0-cf23-4cb8-9fcb-152ae4fd1e69"
}
```

AWS AppSync 使用以下字符串化的 JSON 对象发回确认消息：
+  `"type": "complete"`：常量 `<string>` 参数。
+  `"id": <string>`：取消注册的订阅的 ID。

在客户端收到确认消息后，它不会接收该特定订阅的更多消息。

示例：

```
{
  "type":"complete",
  "id":"eEXAMPLE-cf23-1234-5678-152EXAMPLE69"
}
```

## 断开连接 WebSocket
<a name="disconnecting-the-websocket"></a>

在断开连接之前，为避免数据丢失，客户端应具有必要的逻辑来检查当前是否未通过 WebSocket连接进行任何操作。在断开与的连接之前，应取消注册所有订阅。 WebSocket

# 正在合并 APIs AWS AppSync
<a name="merged-api"></a>

随着 GraphQL 在组织中的使用范围扩大，可能会在 API ease-of-use 和 API 开发速度之间进行权衡。一方面，组织采用 AWS AppSync 和 GraphQL 来简化应用程序开发。这为开发人员提供了一个灵活的 API，他们可以通过一次网络调用安全地访问、操作和合并来自一个或多个数据域的数据。另一方面，组织内负责合并成单个 GraphQL API 端点的不同数据域的团队可能希望能够独立创建、管理和部署 API 更新。这提高了它们的发育速度。

为了解决这种紧张局势， AWS AppSync 合并 APIs 功能允许来自不同数据域的团队独立创建和部署 AWS AppSync APIs （例如 GraphQL 架构、解析器、数据源和函数），然后可以将其组合成一个合并的 API。这使组织能够维护简单易用的跨域 API，并为对该 API 做出贡献的不同团队提供一种方法，以快速独立地进行 API 更新。

下图显示了合并后的 API 工作流程：

![\[该图显示了合并的 API 工作流程， APIs 其中多个源合并为一个合并的 API 端点\]](http://docs.aws.amazon.com/zh_cn/appsync/latest/devguide/images/merged-api-workflow.png)


使用 Mer APIs ged，组织可以将多个独立来源的资源导 AWS AppSync APIs 入到单个 Merged AWS AppSync API 端点中。为此， AWS AppSync 允许您创建源列表 AWS AppSync APIs，然后将与源关联的所有元数据（ APIs 包括架构、类型、数据源、解析器和函数）合并到一个新的 AWS AppSync 合并的 API 中。

在合并过程中，由于源 API 数据内容不一致（例如，在合并多个架构时发生的类型命名冲突），可能会发生合并冲突。对于源代码中没有定义 APIs 冲突的简单用例，无需修改源 API 架构。生成的 Merged API 只是从原始源导入所有类型、解析器、数据源 AWS AppSync APIs和函数。对于出现冲突的复杂用例 users/teams ，必须通过各种方式解决冲突。 AWS AppSync 为用户提供了多种可以减少合并冲突的工具和示例。

中配置的后续合并会 AWS AppSync 将源代码 APIs 中所做的更改传播到关联的 Merged API。

## 合并 APIs 和联合
<a name="merged-api-federation"></a>

GraphQL 社区中有许多解决方案和模式可用于组合 GraphQL 架构并通过共享图表实现团队协作。 AWS AppSync Mer APIs ge *d 采用构建时*方法进行架构组合，将源代码组合成单独 APIs 的 Merged API。另一种方法是将*运行时*路由器分层到多个源图 APIs 或子图上。在这种方法中，路由器接收请求，引用其作为元数据维护的组合架构，构建请求计划，然后在其底层子图/服务器上分配请求元素。下表比较了 Graph AWS AppSync QL 架构组合的 Merged API 构建时方法和基于路由器的运行时方法：


|  |  |  | 
| --- |--- |--- |
| 功能 | AppSync 合并的 API | 基于路由器的解决方案 | 
| 子图独立管理 | 支持 | 是 | 
| 子图可独立寻址 | 支持 | 是 | 
| 自动架构组合 | 支持 | 是 | 
| 自动冲突检测 | 支持 | 是 | 
| 通过架构指令解决冲突 | 支持 | 是 | 
| 支持的子图服务器 | AWS AppSync\$1 | 变化 | 
| 网络复杂性 | 单个、合并的 API 意味着没有额外的网络跳跃。 | 多层架构需要查询规划和委托、子查询解析和序列化/反序列化，以及在子图中引用解析器来执行联接。 | 
| 可观察性支持 | 内置监控、日志和跟踪。单个 Merged API 服务器意味着简化调试。 | Build-your-own 跨路由器和所有关联的子图服务器的可观察性。跨分布式系统的复杂调试。 | 
| 授权支持 | 内置对多种授权模式的支持。 | Build-your-own 授权规则。 | 
| 跨账户安全 | 内置对跨AWS 云账户关联的支持。 | Build-your-own 安全模型。 | 
| 订阅支持 | 是 | 否 | 

\$1 M AWS AppSync er APIs ged 只能与 AWS AppSync 源关联 APIs。如果您需要支持跨子图 AWS AppSync 和非AWS AppSync 子图的架构组合，则可以将一个或多个 AWS AppSync G and/or raphQL Merged 连接到基于 APIs 路由器的解决方案。例如，参见参考博客，了解如何在 Apollo Feder AWS AppSync APIs ation v2：Apollo Grap [h](https://aws.amazon.com/blogs/mobile/federation-appsync-subgraph/) QL Federation 中使用基于路由器的架构添加为子图。 AWS AppSync

**Topics**
+ [合并 APIs 和联合](#merged-api-federation)
+ [解决合并的 API 冲突](#merged-api-conflict-resolution)
+ [配置架构](#configuring-schemas-merged-api)
+ [配置授权模式](#configuring-authorization-merged-api)
+ [配置执行角色](#execution-roles-merged-api)
+ [使用配置跨账户 Merged APIs AWS RAM](#cross-account-merged-api)
+ [合并](#merges)
+ [对 Merged 的额外支持 APIs](#merge-api-additional-support)
+ [合并的 API 限制](#merged-api-limits)
+ [合并的 API 注意事项](#merged-api-considerations)
+ [创建合并 APIs](#creating-merged-api)

## 解决合并的 API 冲突
<a name="merged-api-conflict-resolution"></a>

在出现合并冲突时， AWS AppSync 为用户提供了多种工具和示例，以帮助解决问题。

### 合并的 API 架构指令
<a name="merged-api-schema-directive"></a>

 AWS AppSync 引入了几个 GraphQL 指令，可用于减少或解决源代码间的冲突： APIs
+ *@canonical*：该指令设置具有类似名称和数据的类型/字段的优先级。如果两个或多个源 APIs 具有相同的 GraphQL 类型或字段，则其中一个源 APIs 可以将其类型或字段注释为 *canonical*，这将在合并期间进行优先排序。合并时 types/fields ，在其他来源中未使用此指令注释的冲突 APIs 将被忽略。
+ *@hidden*：该指令封装了某些内容 types/fields ，可以将其从合并过程中删除。团队可能希望删除或隐藏源 API 中的特定类型或操作，以便仅内部客户端可以访问特定类型的数据。在附加该指令后，类型或字段不会合并到合并的 API 中。
+ *@renamed*：该指令更改了的名称 types/fields 以减少命名冲突。在某些情况下 APIs ，不同的类型或字段名称相同。不过，需要在合并的架构中提供所有这些 API。要将它们全部包含在合并的 API 中，一个简单方法是将字段重命名为类似但不同的名称。

要显示架构指令提供的功能，请考虑以下示例：

在这个例子中，假设我们要合并两个源 APIs。我们有两个创建和检索文章（例如，评论部分或社交媒体文章）的架构。假设这些类型和字段非常相似，在合并操作期间很可能会发生冲突。下面的代码片段显示每个架构的类型和字段。

第一个文件名为 *Source1.graphql*，它是一个 GraphQL 架构，允许用户使用 `putPost` 变更创建 `Posts`。每个 `Post` 包含一个标题和 ID。ID 用于引用 `User` 或发布者信息（电子邮件和地址）以及 `Message` 或负载（内容）。`User` 类型使用 *@canonical* 标签进行注释。

```
# This snippet represents a file called Source1.graphql

type Mutation {
    putPost(id: ID!, title: String!): Post
}

type Post {
    id: ID!
    title: String!
}

type Message {
   id: ID!
   content: String
}

type User @canonical {
   id: ID!
   email: String!
   address: String!
}

type Query {
    singlePost(id: ID!): Post
    getMessage(id: ID!): Message
}
```

第二个文件名为 *Source2.graphql*，它是一个 GraphQL 架构，其功能与 *Source1.graphql* 非常相似。但请注意，每种类型的字段是不同的。在合并这两个架构时，由于这些差异，将会发生合并冲突。

另请注意，*Source2.graphql* 还包含多个指令以减少这些冲突。`Post` 类型使用 *@hidden* 标签进行注释，以在合并操作期间对其自身进行模糊处理。`Message` 类型使用 *@renamed* 标签进行注释，以便在与另一个 `Message` 类型发生命名冲突时将类型名称修改为 `ChatMessage`。

```
# This snippet represents a file called Source2.graphql

type Post @hidden  {
    id: ID!
    title: String!
    internalSecret: String!
}

type Message @renamed(to: "ChatMessage") {
   id: ID!
   chatId: ID!
   from: User!
   to: User!
}

# Stub user so that we can link the canonical definition from Source1
type User {
   id: ID!
}

type Query {
    getPost(id: ID!): Post
    getMessage(id: ID!): Message @renamed(to: "getChatMessage")
}
```

在发生合并时，结果将生成 `MergedSchema.graphql` 文件：

```
# This snippet represents a file called MergedSchema.graphql

type Mutation {
    putPost(id: ID!, title: String!): Post
}

# Post from Source2 was hidden so only uses the Source1 definition. 
type Post {
    id: ID!
    title: String!
}

# Renamed from Message to resolve the conflict
type ChatMessage {
   id: ID!
   chatId: ID!
   from: User!
   to: User!
}

type Message {
   id: ID!
   content: String
}

# Canonical definition from Source1
type User {
   id: ID!
   email: String!
   address: String!
}

type Query {
    singlePost(id: ID!): Post
    getMessage(id: ID!): Message
    
    # Renamed from getMessage
    getChatMessage(id: ID!): ChatMessage
}
```

在合并过程中发生了以下情况：
+ 由于使用了 *@canonical* 注释，*Source1.graphql* 中的 `User` 类型优先于 *Source2.graphql* 中的 `User` 类型。
+ *Source1.graphql* 中的 `Message` 类型包含在合并中。不过，*Source2.graphql* 中的 `Message` 存在命名冲突。由于它具有 *@renamed* 注释，它也包含在合并中，但具有替代名称 `ChatMessage`。
+ 将包含 *Source1.graphql* 中的 `Post` 类型，但不包含 *Source2.graphql* 中的 `Post` 类型。通常，该类型将会发生冲突，但由于 *Source2.graphql* 中的 `Post` 类型具有 *@hidden* 注释，将对其数据进行模糊处理而不会包含在合并中。这不会导致任何冲突。
+ 更新了 `Query` 类型以包含两个文件中的内容。不过，由于该指令，一个 `GetMessage` 查询被命名为 `GetChatMessage`。这解决了两个同名查询之间的命名冲突。

还有一种情况是，不会将任何指令添加到冲突的类型中。此处，合并的类型包括该类型的所有源定义中的所有字段的联合。例如，请考虑以下示例：

该架构名为 *Source1.graphql*，允许创建和检索 `Posts`。配置与上一示例类似，但信息较少。

```
# This snippet represents a file called Source1.graphql

type Mutation {
    putPost(id: ID!, title: String!): Post
}

type Post  {
    id: ID!
    title: String!
}

type Query {
    getPost(id: ID!): Post
}
```

该架构名为 *Source2.graphql*，允许创建和检索 `Reviews`（例如，电影评级或餐厅评价）。`Reviews` 与相同 ID 值的 `Post` 相关联。它们放在一起以提供完整评价文章的标题、文章 ID 和负载消息。

在合并时，将在两种 `Post` 类型之间发生冲突。由于没有可以解决该问题的注释，因此，默认行为是对冲突类型执行联合操作。

```
# This snippet represents a file called Source2.graphql

type Mutation {
    putReview(id: ID!, postId: ID!, comment: String!): Review
}

type Post  {
    id: ID!
    reviews: [Review]
}

type Review {
   id: ID!
   postId: ID!
   comment: String!
}

type Query {
    getReview(id: ID!): Review
}
```

在发生合并时，结果将生成 `MergedSchema.graphql` 文件：

```
# This snippet represents a file called MergedSchema.graphql

type Mutation {
    putReview(id: ID!, postId: ID!, comment: String!): Review
    putPost(id: ID!, title: String!): Post
}

type Post  {
    id: ID!
    title: String!
    reviews: [Review]
}

type Review {
   id: ID!
   postId: ID!
   comment: String!
}

type Query {
    getPost(id: ID!): Post
    getReview(id: ID!): Review
}
```

在合并过程中发生了以下情况：
+ `Mutation` 类型没有遇到冲突并进行了合并。
+ `Post` 类型字段通过联合操作进行合并。请注意两者之间的联合如何生成一个 `id`、`title` 和 `reviews`。
+ `Review` 类型没有遇到冲突并进行了合并。
+ `Query` 类型没有遇到冲突并进行了合并。

### 管理共享类型上的解析器
<a name="resolvers-shared-types-merged-api"></a>

在上面的示例中，考虑 *Source1.graphql* 在 `Query.getPost` 上配置了单位解析器的情况，该解析器使用名为 `PostDatasource` 的 DynamoDB 数据来源。该解析器将返回 `Post` 类型的 `id` 和 `title`。现在，考虑 *Source2.graphql* 在 `Post.reviews` 上配置了管道解析器的情况，该解析器运行两个函数。`Function1` 附加了一个 `None` 数据来源以执行自定义授权检查。`Function2` 附加了一个 DynamoDB 数据来源以查询 `reviews` 表。

```
query GetPostQuery {
    getPost(id: "1") {
        id,
        title,
        reviews
    }
}
```

当客户端对 Merged API 终端节点运行上述查询时，该 AWS AppSync 服务首先运行 from 的单位解析器`Source1`，它会调用`PostDatasource`并`Query.getPost`从 DynamoDB 返回数据。然后，它运行 `Post.reviews` 管道解析器，其中 `Function1` 执行自定义授权逻辑，并且 `Function2` 返回位于 `$context.source` 中的给定 `id` 的评价。该服务将请求作为单个 GraphQL 运行进行处理，并且该简单请求仅需要一个请求令牌。

### 管理共享类型上的解析器冲突
<a name="resolver-conflict-shared-type-merged-api"></a>

考虑以下情况，我们还在 `Query.getPost` 上实施了一个解析器，以便每次在 `Source2` 中的字段解析器以外提供多个字段。*Source1.graphql* 可能如下所示：

```
# This snippet represents a file called Source1.graphql

type Post  {
    id: ID!
    title: String!
    date: AWSDateTime!
}

type Query {
    getPost(id: ID!): Post
}
```

*Source2.graphql* 可能如下所示：

```
# This snippet represents a file called Source2.graphql

type Post  {
  id: ID!
  content: String!
  contentHash: String! 
  author: String! 
}

type Query {
    getPost(id: ID!): Post
}
```

尝试合并这两个架构会生成合并错误，因为 Merge AWS AppSync d APIs 不允许将多个源解析器附加到同一个字段。为了解决该冲突，您可以实施一种字段解析器模式，该模式要求 *Source2.graphql* 添加一个单独的类型以定义它从 `Post` 类型中拥有的字段。在以下示例中，我们添加一个名为 `PostInfo` 的类型，其中包含由 *Source2.graphql* 解析的 content 和 author 字段。*Source1.graphql* 实施附加到 `Query.getPost` 的解析器，而 *Source2.graphql* 现在将一个解析器附加到 `Post.postInfo`，以确保可以成功检索所有数据：

```
type Post  {
  id: ID!
  postInfo: PostInfo
}

type PostInfo {
   content: String!
   contentHash: String!
   author: String!
}

type Query {
    getPost(id: ID!): Post
}
```

虽然解决此类冲突需要重新编写源 API 架构，并且客户端可能需要更改其查询，但这种方法的优点是，合并解析器的所有权在源团队中仍然是清晰的。

## 配置架构
<a name="configuring-schemas-merged-api"></a>

双方负责配置架构以创建合并的 API：
+ **合并的 API 所有者** - 合并的 API 所有者必须配置合并 API 的授权逻辑和高级设置，例如日志记录、跟踪、缓存和 WAF 支持。
+ **关联的源 API 所有者** - 关联的 API 所有者必须配置构成合并 API 的架构、解析器和数据来源。

由于您的 Merged API 架构是根据关联源的架构创建的 APIs，因此它是**只读**的。这意味着对架构的更改必须在源中启动 APIs。在 AWS AppSync 控制台中，您可以使用架构窗口上方的下拉列表在合并架构和合并的 API 中 APIs 包含的源的各个**架构**之间切换。

## 配置授权模式
<a name="configuring-authorization-merged-api"></a>

可以使用多种授权模式保护您的合并 API。要详细了解中的授权模式 AWS AppSync，请参阅[授权和身份验证](https://docs.aws.amazon.com/appsync/latest/devguide/security-authz.html)。

以下授权模式可用于 Merged APIs：
+  **API 密钥**：最简单的授权策略。所有请求必须在 `x-api-key` 请求标头下面包含 API 密钥。过期的 API 密钥在过期日期之后保留 60 天。
+  **AWS 身份和访问管理 (IAM) A** **ccess Management： AWS IAM 授权策略授权所有经过 sigv4 签名的请求。**
+  **Amazon Cognito 用户池**：通过 Amazon Cognito 用户池授权您的用户以实现更精细的控制。
+  **AWS Lambda Author** izers：一种无服务器函数，允许您使用自定义逻辑对您的 AWS AppSync API 进行身份验证和授权。
+ **OpenID Connect**：该授权类型强制实施 OIDC 兼容服务提供的 OpenID Connect (OIDC) 令牌。您的应用程序可以利用由 OIDC 提供程序定义的用户和权限来控制访问。

合并的 API 的授权模式是由合并的 API 所有者配置的。在执行合并操作时，合并的 API 必须包含在源 API 上配置的主要授权模式，以作为自己的主要授权模式或辅助授权模式。否则，将会不兼容，并且合并操作由于冲突而失败。在源代码中使用多重身份验证指令时 APIs，合并过程能够自动将这些指令合并到统一端点中。如果源 API 的主要授权模式与合并 API 的主要授权模式不匹配，它自动添加这些授权指令，以确保源 API 中的类型的授权模式一致。

## 配置执行角色
<a name="execution-roles-merged-api"></a>

在创建合并的 API 时，您需要定义服务角色。 AWS 服务角色是一种 AWS 身份和访问管理 (IAM) Access Management 角色，服务使用 AWS 该角色代表您执行任务。

在这种情况下，您的 Merged API 必须运行解析器，以访问源中配置的数据源 APIs中的数据。为此所需的服务角色是`mergedApiExecutionRole`，并且它必须具有通过 `appsync:SourceGraphQL` IAM 权限对合并后的 API 中 APIs 包含的源运行请求的显式访问权限。在运行 GraphQL 请求期间， AWS AppSync 服务将担任此服务角色并授权该角色执行操作。`appsync:SourceGraphQL`

AWS AppSync 支持在请求中的特定顶级字段上允许或拒绝此权限，例如 IAM 授权模式如何适用于 IAM APIs。对于 non-top-level字段， AWS AppSync 需要您定义对源 API ARN 本身的权限。为了限制对合并 API 中特定 non-top-level字段的访问，我们建议您在 Lambda 中实现自定义逻辑，或者使用 *@hidden* 指令在合并的 API 中隐藏源 API 字段。如果要允许角色在源 API 中执行所有数据操作，您可以添加以下策略。请注意，第一个资源条目允许访问所有顶级字段，第二个条目涵盖对源 API 资源本身进行授权的子解析器：

------
#### [ JSON ]

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [{
        "Effect": "Allow", 
        "Action": [ "appsync:SourceGraphQL"], 
        "Resource": [ 
            "arn:aws:appsync:us-west-2:123456789012:apis/YourSourceGraphQLApiId/*", 
            "arn:aws:appsync:us-west-2:123456789012:apis/YourSourceGraphQLApiId"] 
    }] 
}
```

------

如果要仅限访问特定的顶级字段，您可以使用如下策略：

------
#### [ JSON ]

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [{
        "Effect": "Allow", 
        "Action": [ "appsync:SourceGraphQL"], 
        "Resource": [ 
            "arn:aws:appsync:us-west-2:123456789012:apis/YourSourceGraphQLApiId/types/Query/fields/<Field-1>",
            "arn:aws:appsync:us-west-2:123456789012:apis/YourSourceGraphQLApiId"] 
    }] 
}
```

------

您还可以使用 AWS AppSync 控制台 API 创建向导生成服务角色，以允许您的 Merged API 访问源中配置 APIs 的、与合并后的 API 属于同一账户的资源。如果您的来源与合并 APIs 的 API 不在同一个账户中，则必须先使用 AWS Resource Access Manager (AWS RAM) 共享资源。

## 使用配置跨账户 Merged APIs AWS RAM
<a name="cross-account-merged-api"></a>

在创建 Merged API 时，您可以选择关联已通过 AWS Resource Access Manager (AWS RAM) 共享的其他账户的来源 APIs 。 AWS RAM 帮助您跨 AWS 账户、组织或组织单位 (OUs) 以及与 IAM 角色和用户安全共享资源。

AWS AppSync 与集成 AWS RAM ，以支持通过单个 Merged API APIs 跨多个账户配置和访问源代码。 AWS RAM 允许您创建资源共享或资源容器，以及将为每个资源共享的权限集。您可以在中 AWS AppSync APIs 添加到资源共享 AWS RAM。在资源共享中， AWS AppSync 提供了三种不同的权限集，这些权限集可以与 RAM 中的 AWS AppSync API 相关联：

1. `AWSRAMPermissionAppSyncSourceApiOperationAccess`： AWS RAM 如果未指定其他权限，则在中共享 AWS AppSync API 时添加的默认权限集。此权限集用于与合并的 AWS AppSync API 所有者共享源 API。该权限集包括源 API 的 `appsync:AssociateMergedGraphqlApi` 权限以及在运行时访问源 API 资源所需的 `appsync:SourceGraphQL` 权限。

1. `AWSRAMPermissionAppSyncMergedApiOperationAccess`：在与源 API 所有者共享合并的 API 时，应配置该权限集。此权限集将使源 API 能够配置合并的 API，包括能够将目标委托人 APIs 拥有的任何来源与合并的 API 关联以及读取和更新合并的 API 的源 API 关联。

1. `AWSRAMPermissionAppSyncAllowSourceGraphQLAccess`：此权限集允许该`appsync:SourceGraphQL`权限与 AWS AppSync API 一起使用。它旨在用于与合并的 API 所有者共享源 API。与源 API 操作访问权限的默认权限集相反，该权限集仅包括运行时权限 `appsync:SourceGraphQL`。如果用户选择与源 API 所有者共享合并的 API 操作访问权限，他们还需要从源 API 中将该权限与合并的 API 所有者共享，以便通过合并的 API 终端节点进行运行时访问。

AWS AppSync 还支持客户管理的权限。当提供的 AWS托管权限之一不起作用时，您可以创建自己的客户管理权限。客户管理的权限是您通过精确指定在哪些条件下可以执行哪些操作并使用 AWS RAM共享资源来创建和维护的托管权限。 AWS AppSync 允许您在创建自己的权限时从以下操作中进行选择：

1. `appsync:AssociateSourceGraphqlApi`

1. `appsync:AssociateMergedGraphqlApi`

1. `appsync:GetSourceApiAssociation`

1. `appsync:UpdateSourceApiAssociation`

1. `appsync:StartSchemaMerge`

1. `appsync:ListTypesByAssociation`

1. `appsync:SourceGraphQL`

在 AWS RAM 中正确共享源 API 或合并的 API 并在必要时接受资源共享邀请后，当您在 Merged API 上创建或更新源 API 关联时，资源共享邀请将在 AWS AppSync 控制台中显示。无论权限设置如何 AWS AppSync APIs ，您还可以通过调 AWS RAM 用提供的`ListGraphqlApis`操作 AWS AppSync 并使用所有`OTHER_ACCOUNTS`者筛选器列出使用您的账户共享的所有内容。

**注意**  
通过共享 AWS RAM 需要调用方拥有 AWS RAM 对正在共享的任何 API 执行`appsync:PutResourcePolicy`操作的权限。

## 合并
<a name="merges"></a>

### 管理合并
<a name="managing-merges"></a>

Mer APIs ged 旨在支持团队在统一 AWS AppSync 端点上进行协作。团队可以在后端独立开发自己的隔离源 GraphQL APIs ，而该 AWS AppSync 服务则管理将资源集成到单个 Merged API 端点中，以减少协作中的摩擦并缩短开发交付时间。

### 自动合并
<a name="auto-merge"></a>

可以将与 AWS AppSync 合并的 API APIs 关联的源配置为在源 API 进行任何更改后自动合并（自动合并）到合并的 API 中。这会确保源 API 中的更改始终在后台传播到合并的 API 终端节点。将在合并的 API 中更新源 API 架构中的任何更改，只要它不会与合并 API 中的现有定义发生合并冲突。如果源 API 中的更新将更新解析器、数据来源或函数，则也会更新导入的资源。在引入无法自动解决的新冲突时，将拒绝合并的 API 架构更新，因为在合并操作期间发生不支持的冲突。对于状态为 `MERGE_FAILED` 的每个源 API 关联，将在控制台中显示错误消息。您也可以使用 AWS SDK 或使用 AWS CLI 调用给定源 API 关联的`GetSourceApiAssociation`操作来检查错误消息，如下所示：

```
aws appsync get-source-api-association --merged-api-identifier <Merged API ARN> --association-id <SourceApiAssociation id>
```

这会生成以下格式的结果：

```
{
    "sourceApiAssociation": {
        "associationId": "<association id>",
        "associationArn": "<association arn>",
        "sourceApiId": "<source api id>",
        "sourceApiArn": "<source api arn>",
        "mergedApiArn": "<merged api arn>",
        "mergedApiId": "<merged api id>",
        "sourceApiAssociationConfig": {
            "mergeType": "MANUAL_MERGE"
        },
        "sourceApiAssociationStatus": "MERGE_FAILED",
        "sourceApiAssociationStatusDetail": "Unable to resolve conflict on object with name title: Merging is not supported for fields with different types."
    }
}
```

### 手动合并
<a name="manual-merges"></a>

源 API 的默认设置是手动合并。要合并自上次更新 Merged API APIs 以来源中发生的任何更改，源 API 所有者可以从 AWS AppSync 控制台或通过 AWS SDK 和 AWS CLI 中提供的`StartSchemaMerge`操作调用手动合并。

## 对 Merged 的额外支持 APIs
<a name="merge-api-additional-support"></a>

### 配置订阅
<a name="config-subscription"></a>

与基于路由器的 GraphQL 架构组合方法不同，Merged 为 GraphQ AWS AppSync L APIs 订阅提供了内置支持。在关联源中定义的所有订阅操作都 APIs 将自动合并，并在合并的 API 中运行，无需修改。要详细了解如何 AWS AppSync 支持通过无服务器 WebSockets 连接进行订阅，请参阅[实时数据](https://docs.aws.amazon.com/appsync/latest/devguide/aws-appsync-real-time-data.html)。

### 配置可观测性
<a name="config-observability"></a>

AWS AppSync Merged 通过 [Amazon APIs ](https://docs.aws.amazon.com/appsync/latest/devguide/monitoring.html) 提供内置日志、监控和指标 CloudWatch。 AWS AppSync 还提供了对通过进行跟踪的内置支持[AWS X-Ray](https://docs.aws.amazon.com/appsync/latest/devguide/x-ray-tracing.html)。

### 配置自定义域
<a name="config-custom-domain"></a>

AWS AppSync Merged APIs 为在合并的 API 的 [GraphQL 和实时](https://docs.aws.amazon.com/appsync/latest/devguide/custom-domain-name.html)端点中使用自定义域提供了内置支持。

### 配置缓存
<a name="config-caching"></a>

AWS AppSync Merged APIs 为可选缓存请求级别和/或解析器级别的响应以及响应压缩提供了内置支持。要了解更多信息，请参阅[缓存和压缩](https://docs.aws.amazon.com/appsync/latest/devguide/enabling-caching.html)。

### 配置私有 APIs
<a name="config-private-api"></a>

AWS AppSync Merged APIs 提供对 Private 的内置支持 APIs ，将对合并的 API 的 GraphQL 和实时终端节点的访问权限限制为来自[您可以配置的 VPC 终端节点的](https://docs.aws.amazon.com/appsync/latest/devguide/using-private-apis.html)流量。

### 配置防火墙规则
<a name="config-firewall"></a>

AWS AppSync Merged APIs 提供了对的内置支持 AWS WAF，使您能够 APIs 通过定义 [Web 应用程序防火墙规则](https://docs.aws.amazon.com/appsync/latest/devguide/WAF-Integration.html)来保护自己的。

### 配置审核日志
<a name="config-audit"></a>

AWS AppSync Merged APIs 提供了对的内置支持 AWS CloudTrail，使您能够[配置和管理审核日志](https://docs.aws.amazon.com/appsync/latest/devguide/cloudtrail-logging.html)。

## 合并的 API 限制
<a name="merged-api-limits"></a>

在开发 Mer APIs ged 时，请注意以下规则：

1. 合并的 API 不能是另一个合并 API 的源 API。

1. 一个源 API 不能与多个合并的 API 相关联。

1. 合并的 API 架构文档的默认大小限制为 10 MB。

1. 可以与合并的 API 关联的源 APIs 默认数量为 10。但是，如果您在合并的 API APIs 中需要超过 10 个来源，则可以请求提高限制。

## 合并的 API 注意事项
<a name="merged-api-considerations"></a>

在设计和实现 Merged 时 APIs，请考虑以下几点：

将多个源合 APIs 并为一个端点可能会增加 GraphQL 架构和查询的大小和复杂性。随着合并架构的增长，查询可能需要遍历多个解析器才能满足单个请求，这可能会增加总请求时间的延迟。例如，访问来自多个来源的字段的查询 APIs 可能需要 AWS AppSync 按顺序执行来自每个源 API 的解析器，每个解析器都会增加总响应时间。

我们强烈建议您在开发期间和实际负载条件下对合并版进行 APIs 全面测试，以确保它们满足您的业务需求。请特别注意：
+ 合并架构的深度和复杂性，尤其是跨多个源访问字段的查询 APIs。
+ 为满足常见查询模式而必须执行的解析器数量。
+ 您的数据源和解析器在预期负载下的性能特征。
+ 跨多个来源访问资源时网络延迟的影响 APIs。

考虑实施性能优化，例如缓存、批处理数据源请求，以及设计源 API 架构，以最大限度地减少常见操作所需的解析器执行次数。

## 创建合并 APIs
<a name="creating-merged-api"></a>

**在控制台中创建合并的 API**

1. 登录 AWS 管理控制台 并打开[AWS AppSync 控制台](https://console.aws.amazon.com/appsync/)。

   1. 在**控制面板**中，选择**创建 API**。

1. 选择 **Merged API**，然后选择**下一步**。

1. 在**指定 API 详细信息**页面中，输入以下信息：

   1. 在 **API 详细信息**下面，输入以下信息：

      1. 指定合并 API 的 **API 名称**。此字段用于标记您的 GraphQL API，以便于将其与其他 GraphQL 区分开来。 APIs

      1. 指定**联系信息**。该字段是可选的，并将名称或组附加到 GraphQL API。它不会链接到其他资源或由其他资源生成，其工作方式与 API 名称字段非常相似。

   1. 在 “**服务角色**” 下，您必须将 IAM 执行角色附加到合并的 API，这样 AWS AppSync 才能在运行时安全地导入和使用您的资源。您可以选择**创建和使用新的服务角色**，这将允许您指定要使用的策略和资源。 AWS AppSync 您也可以选择**使用现有的服务角色**，然后从下拉列表中选择现有 IAM 角色以将其导入。

   1. 在**私有 API 配置**下面，您可以选择启用私有 API 功能。请注意，在创建合并的 API 后，无法更改该选项。有关私有的更多信息 APIs，请参阅[使用 AWS AppSync 私有 APIs](https://docs.aws.amazon.com/appsync/latest/devguide/using-private-apis.html)。

      在完成后，选择**下一步**。

1. 接下来，您必须添加将用作合并后 APIs 的 API 基础的 GraphQL。在 **“选择来源 APIs**” 页中，输入以下信息：

   1. 在 “**APIs 来自您的 AWS 账户**” 表中，选择 “**添加来源**” APIs。在 GraphQL 列表中 APIs，每个条目都将包含以下数据：

      1. **名称**：GraphQL API 的 **API 名称**字段。

      1. **API ID**：GraphQL API 的唯一 ID 值。

      1. **主要授权模式**：GraphQL API 的默认授权模式。有关 AWS AppSync中的授权模式的更多信息，请参阅[授权和身份验证](https://docs.aws.amazon.com/appsync/latest/devguide/security-authz.html)。

      1. **额外的授权模式**：在 GraphQL API 中配置的辅助授权模式。

      1. 选中 APIs API **名称**字段旁边的复选框，选择要在合并的 API 中使用的。然后，选择 “**添加来源**” APIs。选定的 GraphQL APIs 将显示在**APIs 您的 AWS 账户表**中。

   1. 在 “**APIs 来自其他 AWS 账户**” 表中，选择 “**添加来源**” APIs。此列表 APIs 中的 GraphQL 来自其他通过 AWS Resource Access Manager ()AWS RAM与您共享资源的账户。在此表 APIs 中选择 GraphQL 的过程与上一节中的过程相同。有关通过共享资源的更多信息 AWS RAM，请参阅[什么是 AWS Resource Access Manager？](https://docs.aws.amazon.com/ram/latest/userguide/what-is.html) 。

      在完成后，选择**下一步**。

   1. 添加您的主要授权模式。有关更多信息，请参阅[授权和身份验证](https://docs.aws.amazon.com/appsync/latest/devguide/security-authz.html)。选择**下一步**。

   1. 检查您的输入，然后选择**创建 API**。

# 使用 RDS 内省构建 GraphQ APIs L
<a name="rds-introspection"></a>

AWS AppSync的自省实用程序可以从数据库表中发现模型并建议 GraphQL 类型。 AWS AppSync 控制台的 “创建 API” 向导可以立即从 Aurora MySQL 或 PostgreSQL 数据库生成 API。它会自动创建用于读取和写入数据的类型和 JavaScript 解析器。

AWS AppSync 通过亚马逊 RDS 数据 API 提供与亚马逊 Aurora 数据库的直接集成。Amazon RDS 数据 API 不要求永久数据库连接，而是提供了一个安全 HTTP 终端节点，可以 AWS AppSync 连接到该终端节点以运行SQL语句。您可以利用这一点在 Aurora 上为您的 MySQL 和 PostgreSQL 工作负载创建关系数据库 API。

使用为关系数据库构建 API AWS AppSync 有以下几个优点：
+ 您的数据库不会直接暴露给客户端，从而使接入点与数据库本身分离。
+ 您可以根据不同应用程序的需求 APIs 量身定制构建，从而无需在前端使用自定义业务逻辑。这与 Backend-For-Frontend (BFF) 模式一致。
+ 可以使用各种授权模式在 AWS AppSync 层上实现授权和访问控制，以控制访问权限。无需额外的计算资源即可连接到数据库，例如托管 Web 服务器或代理连接。
+ 可以通过订阅添加实时功能，通过 AppSync 自动将数据变更推送到连接的客户端。
+ 客户端可以使用常用端口（例如 443）通过 HTTPS 连接到 API。

AWS AppSync 使基于现有关系数据库 APIs 的构建变得容易。它的自省实用程序可以从数据库表中发现模型并建议 GraphQL 类型。 AWS AppSync 控制台的*创建 API* 向导可以立即从 Aurora MySQL 或 PostgreSQL 数据库生成 API。它会自动创建用于读取和写入数据的类型和 JavaScript解析器。

AWS AppSync 提供了用于简化在解析器中编写 SQL 语句的集成 JavaScript 实用程序。您可以将 AWS AppSync的`sql`标签模板用于具有动态值的静态语句，也可以使用`rds`模块实用程序以编程方式生成语句。有关更多信息，请参阅 [RDS 的解析器函数参考](https://docs.aws.amazon.com//appsync/latest/devguide/resolver-reference-rds-js.html)数据来源和[内置模块](https://docs.aws.amazon.com//appsync/latest/devguide/built-in-modules-js.html#built-in-rds-modules)。

## 使用自省特征（控制台）
<a name="using-introspection-console"></a>

有关详细教程和入门指南，请参阅[教程：带数据 API 的 Aurora PostgreSQL Serverless](https://docs.aws.amazon.com//appsync/latest/devguide/aurora-serverless-tutorial-js.html)。

 AWS AppSync 控制台允许您在短短几分钟内从配置了数据 API 的现有 Aurora 数据库创建 AWS AppSync GraphQL API。这会根据您的数据库配置快速生成操作架构。您可以按原样使用 API，也可以在它的基础之上构建来添加特征。

1. 登录 AWS 管理控制台 并打开[AppSync控制台](https://console.aws.amazon.com/appsync/)。

   1. 在**控制面板**中，选择**创建 API**。

1. **在 **API 选项下，选择** **GraphQL APIs**、**从亚马逊 Aurora 集群开始**，然后选择 “下一步”。**

   1. 输入 **API 名称**。这将在控制台中用作 API 的标识符。

   1. 对于**联系信息**，您可以输入联系人以指定 API 的管理员。此为可选字段。

   1. 在**私有 API 配置**下面，您可以启用私有 API 功能。只能从配置的 VPC 终端节点 (VPCE) 中访问私有 API。有关更多信息，请参阅[私有 APIs](https://docs.aws.amazon.com//appsync/latest/devguide/using-private-apis.html)。

      对于该示例，我们不建议启用该功能。在检查您的输入后，选择**下一步**。

1. 在**数据库**页面中，选择**选择数据库**。

   1. 您需要从集群中选择数据库。第一步是选择您的集群所在的**区域**。

   1. 从下拉列表中选择 **Aurora 集群**。请注意，在使用资源之前，您必须已创建并[启用](https://docs.aws.amazon.com//AmazonRDS/latest/AuroraUserGuide/data-api.html#data-api.enabling)相应的数据 API。

   1. 接下来，您必须将数据库的凭证添加到服务中。这主要是使用来完成的 AWS Secrets Manager。选择您的密钥所在的**区域**。有关如何检索密钥信息的更多信息，请参阅[查找密钥](https://docs.aws.amazon.com//secretsmanager/latest/userguide/manage_search-secret.html)或[检索密钥](https://docs.aws.amazon.com//secretsmanager/latest/userguide/retrieving-secrets.html)。

   1. 从下拉列表中添加您的密钥。请注意，用户必须对您的数据库具有[读取权限](https://docs.aws.amazon.com//AmazonRDS/latest/UserGuide/security_iam_id-based-policy-examples.html#security_iam_id-based-policy-examples-console)。

1. 选择**导入**。

   AWS AppSync 将开始内省您的数据库，发现表、列、主键和索引。它会检查 GraphQL API 是否支持所发现的表。请注意，为了支持创建新行，表需要一个可以使用多列的主键。 AWS AppSync 将表列映射到类型字段，如下所示：    
[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/appsync/latest/devguide/rds-introspection.html)

1. 表发现完成后，**数据库**部分将填充您的信息。在新的**数据库表**部分中，表中的数据可能已经填充并转换为架构的类型。如果您没有看到某些必填数据，则可以通过以下方式进行检查：选择**添加表**，在出现的模式中单击这些类型的复选框，然后选择**添加**。

   要从**数据库表**部分删除类型，请单击要删除的类型旁边的复选框，然后选择**删除**。如果您想稍后再次添加，删除的类型将置于**添加表**模式中。

   请注意， AWS AppSync 使用表名作为类型名称，但您可以对其进行重命名，例如，将复数表名更改*movies*为类型名称*Movie*。要重命名**数据库表**部分中的类型，请单击要重命名的类型的复选框，然后单击**类型名称**列中的*铅笔*图标。

   要根据您的选择预览架构的内容，请选择**预览架构**。请注意，此架构不能为空，因此必须至少有一个表转换为类型。此外，此架构的大小不能超过 1MB。

   1. 在**服务角色**下，选择是专门为此导入创建新的服务角色，还是使用现有的角色。

1. 选择**下一步**。

1. 接下来，选择是创建只读 API（仅限查询），还是创建用于读取和写入数据（包含查询和突变）的 API。后者还支持由突变触发的实时订阅。

1. 选择**下一步**。

1. 查看您的选择，然后选择**创建 API**。 AWS AppSync 将创建 API 并将解析器附加到查询和突变。生成的 API 可完全运行，可以根据需要进行扩展。

## 使用自省特征 (API)
<a name="using-introspection-api"></a>

您可以使用 `StartDataSourceIntrospection` 自省 API 以编程方式发现数据库中的模型。有关该命令的更多详细信息，请参阅“使用 [https://docs.aws.amazon.com//appsync/latest/APIReference/API_StartDataSourceIntrospection.html](https://docs.aws.amazon.com//appsync/latest/APIReference/API_StartDataSourceIntrospection.html) API”。

要使用`StartDataSourceIntrospection`，请提供您的 Aurora 集群亚马逊资源名称 (ARN)、数据库名称和密钥 AWS Secrets Manager ARN。该命令启动自省过程。您可以使用 `GetDataSourceIntrospection` 命令检索结果。您可以指定该命令是否应返回已发现模型的存储定义语言 (SDL) 字符串。这对于直接从已发现的模型生成 SDL 架构定义非常有用。

 例如，如果您对简单 `Todos` 表使用以下数据定义语言 (DDL) 语句：

```
create table if not exists public.todos  
(  
id serial constraint todos_pk primary key,  
description text,  
due timestamp,  
"createdAt" timestamp default now()  
);
```

您从以下内容开始自省。

```
aws appsync start-data-source-introspection \ 
  --rds-data-api-config resourceArn=<cluster-arn>,secretArn=<secret-arn>,databaseName=database
```

接下来，使用 `GetDataSourceIntrospection` 命令检索结果。

```
aws appsync get-data-source-introspection \
  --introspection-id a1234567-8910-abcd-efgh-identifier \
  --include-models-sdl
```

这会返回以下结果。

```
{
    "introspectionId": "a1234567-8910-abcd-efgh-identifier",
    "introspectionStatus": "SUCCESS",
    "introspectionStatusDetail": null,
    "introspectionResult": {
        "models": [
            {
                "name": "todos",
                "fields": [
                    {
                        "name": "description",
                        "type": {
                            "kind": "Scalar",
                            "name": "String",
                            "type": null,
                            "values": null
                        },
                        "length": 0
                    },
                    {
                        "name": "due",
                        "type": {
                            "kind": "Scalar",
                            "name": "AWSDateTime",
                            "type": null,
                            "values": null
                        },
                        "length": 0
                    },
                    {
                        "name": "id",
                        "type": {
                            "kind": "NonNull",
                            "name": null,
                            "type": {
                                "kind": "Scalar",
                                "name": "Int",
                                "type": null,
                                "values": null
                            },
                            "values": null
                        },
                        "length": 0
                    },
                    {
                        "name": "createdAt",
                        "type": {
                            "kind": "Scalar",
                            "name": "AWSDateTime",
                            "type": null,
                            "values": null
                        },
                        "length": 0
                    }
                ],
                "primaryKey": {
                    "name": "PRIMARY_KEY",
                    "fields": [
                        "id"
                    ]
                },
                "indexes": [],
                "sdl": "type todos\n{\ndescription: String\n\ndue: AWSDateTime\n\nid: Int!\n\ncreatedAt: AW
SDateTime\n}\n"
            }
        ],
        "nextToken": null
    }
}
```