

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

# AWS AppSync JavaScript 解析器概述
<a name="resolver-reference-overview-js"></a>

AWS AppSync 允许您通过对数据源执行操作来响应 GraphQL 请求。对于您希望运行查询、变更或订阅的每个 GraphQL 字段，必须附加一个解析器。

解析器是 GraphQL 和数据来源之间的连接器。它们讲述 AWS AppSync 如何将传入的 GraphQL 请求转换为后端数据源的指令，以及如何将来自该数据源的响应转换回 GraphQL 响应。使用 AWS AppSync，您可以使用编写解析器 JavaScript并在 AWS AppSync (`APPSYNC_JS`) 环境中运行它们。

AWS AppSync 允许您在管道中编写由多个 AWS AppSync 函数组成的单元解析器或管道解析器。

## 支持的运行时系统功能
<a name="runtime-support-js"></a>

 AWS AppSync JavaScript 运行时提供了一部分 JavaScript 库、实用程序和功能。有关`APPSYNC_JS`运行时支持的特性和功能的完整列表，请参阅[解析器和函数的JavaScript 运行时特性](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-util-reference-js.html)。

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

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

```
import * as ddb from '@aws-appsync/utils/dynamodb'

export function request(ctx) {
  return ddb.get({ key: { id: ctx.args.id } });
}

export const response = (ctx) => ctx.result;
```

## JavaScript 管道解析器的剖析
<a name="anatomy-of-a-pipeline-resolver-js"></a>

管道解析器由定义请求和响应处理程序以及函数列表的代码组成。每个函数具有一个对数据来源执行的**请求**和**响应**处理程序。由于管道解析器将运行委派给一组函数，因此，它不会链接到任何数据来源。单位解析器和函数是对数据来源执行操作的基元。

### 管道解析器请求处理程序
<a name="request-handler-js"></a>

管道解析器的请求处理程序（预备步骤）允许您在运行定义的函数之前执行一些准备逻辑。

### 函数列表
<a name="functions-list-js"></a>

管道解析器将按顺序运行的函数的列表。管道解析器请求处理程序评估结果作为 `ctx.prev.result` 提供给第一个函数。每个函数评估结果作为 `ctx.prev.result` 提供给下一个函数。

### 管道解析器响应处理程序
<a name="response-handler-js"></a>

管道解析器的响应处理程序允许您执行从最后一个函数的输出到预期 GraphQL 字段类型的一些最终逻辑。函数列表中的最后一个函数的输出在管道解析器响应处理程序中作为 `ctx.prev.result` 或 `ctx.result` 提供。

### 执行流程
<a name="execution-flow-js"></a>

假定一个管道解析器由两个函数组成，下面的列表表示调用解析器时的执行流程：

1.  管道解析器请求处理程序

1.  函数 1：函数请求处理程序 

1.  函数 1：数据来源调用 

1.  函数 1：函数响应处理程序 

1.  函数 2：函数请求处理程序 

1.  函数 2：数据来源调用 

1.  函数 2：函数响应处理程序 

1.  管道解析器响应处理程序 

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


### 非常有用的 `APPSYNC_JS` 运行时系统内置实用程序
<a name="useful-utilities-js"></a>

在使用管道解析器时，以下实用工具可为您提供帮助。

#### ctx.stash
<a name="ctx-stash-js"></a>

存储区是一个在每个解析器以及函数请求和响应处理程序中提供的对象。相同的存储区实例在单次解析器运行时间内有效。这意味着，您可以使用存储区在请求和响应处理程序之间以及管道解析器中的函数之间传送任意数据。你可以像普通 JavaScript 物体一样测试藏匿处。

#### ctx.prev.result
<a name="ctx-prev-result-js"></a>

`ctx.prev.result` 表示已在管道中执行的上一个操作的结果。如果上一个操作是管道解析器请求处理程序，则将 `ctx.prev.result` 提供给链中的第一个函数。如果上一个操作是第一个函数，则 `ctx.prev.result` 表示第一个函数的输出，并且可供管道中的第二个函数使用。如果上一个操作是最后一个函数，则 `ctx.prev.result` 表示最后一个函数的输出，并提供给管道解析器响应处理程序。

#### util.error
<a name="util-error-js"></a>

`util.error` 实用工具对于引发字段错误很有用。在函数请求或响应处理程序中使用 `util.error` 将立即引发字段错误，这会禁止执行后续的函数。有关更多详细信息和其他`util.error`签名，请访问[解析器和函数的JavaScript运行时功能](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-util-reference-js.html)。

#### util.appendError
<a name="util-appenderror-js"></a>

`util.appendError` 与 `util.error()` 类似，主要区别在于，它不会中断处理程序评估。相反，它指示字段存在错误，但允许评估处理程序并因而返回数据。在函数中使用 `util.appendError` 将不会中断管道的执行流。有关更多详细信息和其他`util.error`签名，请访问解[析器和函数的JavaScript 运行时功能](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-util-reference-js.html)。

#### runtime.earlyReturn
<a name="runtime-earlyreturn-js"></a>

`runtime.earlyReturn` 函数允许您从任何请求函数中提前返回。在解析器请求处理程序中使用 `runtime.earlyReturn` 将从解析器中返回。从 AWS AppSync 函数请求处理程序中调用它将从该函数中返回，并继续运行到管道中的下一个函数或解析器响应处理程序。

### 编写管道解析器
<a name="writing-resolvers"></a>

管道解析器还具有运行管道中的函数之前和之后的请求和响应处理程序：其请求处理程序在第一个函数的请求之前运行，其响应处理程序在最后一个函数的响应之后运行。解析器请求处理程序可以设置管道中的函数使用的数据。解析器响应处理程序负责返回映射到 GraphQL 字段输出类型的数据。在以下示例中，解析器请求处理程序定义 `allowedGroups`；返回的数据应属于这些组之一。解析器的函数可以使用该值以请求数据。解析器的响应处理程序进行最终检查并筛选结果，以确保仅返回属于允许的组的项目。

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

/**
 * Called before the request function of the first AppSync function in the pipeline.
 *  @param ctx the context object holds contextual information about the function invocation.
 */
export function request(ctx) {
  ctx.stash.allowedGroups = ['admin'];
  ctx.stash.startedAt = util.time.nowISO8601();
  return {};
}
/**
 * Called after the response function of the last AppSync function in the pipeline.
 * @param ctx the context object holds contextual information about the function invocation.
 */
export function response(ctx) {
  const result = [];
  for (const item of ctx.prev.result) {
    if (ctx.stash.allowedGroups.indexOf(item.group) > -1) result.push(item);
  }
  return result;
}
```

#### 写入 AWS AppSync 函数
<a name="writing-functions"></a>

AWS AppSync 函数使您能够编写通用逻辑，这些逻辑可以在架构中的多个解析器中重复使用。例如，您可以设置一个名为的函数`QUERY_ITEMS`，该 AWS AppSync 函数负责查询来自 Amazon DynamoDB 数据源的项目。对于要用于查询项目的解析器，只需将函数添加到解析器的管道并提供要使用的查询索引即可。不必重新实施该逻辑。

## 补充主题
<a name="supplemental-topics"></a>

**主题**
+ [使用 Amazon DynamoDB 的管线解析器示例](https://docs.aws.amazon.com/appsync/latest/devguide/writing-code.html)
+ [为 `APPSYNC_JS` 运行时配置实用程序](https://docs.aws.amazon.com/appsync/latest/devguide/utility-resolvers.html)
+ [为运行时打包 TypeScript、和源映射 `APPSYNC_JS`](https://docs.aws.amazon.com/appsync/latest/devguide/additional-utilities.html)
+ [测试解析器和函数处理程序](https://docs.aws.amazon.com/appsync/latest/devguide/test-resolvers.html)
+ [从 VTL 迁移到 JavaScript](https://docs.aws.amazon.com/appsync/latest/devguide/migrating-resolvers.html)
+ [在直接数据来源访问和通过 Lambda 数据来源代理之间进行选择](https://docs.aws.amazon.com/appsync/latest/devguide/choosing-data-source.html)

# 使用 Amazon DynamoDB 的管线解析器示例
<a name="writing-code"></a>

假设您希望在名为 `getPost(id:ID!)` 的字段上附加一个管道解析器，该解析器使用以下 GraphQL 查询从 Amazon DynamoDB 数据来源中返回 `Post` 类型：

```
getPost(id:1){
    id
    title
    content
}
```

首先，使用下面的代码将一个简单的解析器附加到 `Query.getPost` 中。这是一个简单的解析器代码示例。在请求处理程序中没有定义任何逻辑，响应处理程序只是返回最后一个函数的结果。

```
/**
 * Invoked **before** the request handler of the first AppSync function in the pipeline.
 * The resolver `request` handler allows to perform some preparation logic
 * before executing the defined functions in your pipeline.
 * @param ctx the context object holds contextual information about the function invocation.
 */
export function request(ctx) {
  return {}
}

/**
 * Invoked **after** the response handler of the last AppSync function in the pipeline.
 * The resolver `response` handler allows to perform some final evaluation logic
 * from the output of the last function to the expected GraphQL field type.
 * @param ctx the context object holds contextual information about the function invocation.
 */
export function response(ctx) {
  return ctx.prev.result
}
```

接下来，定义从数据来源中检索 postitem 的 `GET_ITEM` 函数：

```
import { util } from '@aws-appsync/utils'
import * as ddb from '@aws-appsync/utils/dynamodb'

/**
 * Request a single item from the attached DynamoDB table datasource
 * @param ctx the context object holds contextual information about the function invocation.
 */
export function request(ctx) {
	const { id } = ctx.args
	return ddb.get({ key: { id } })
}

/**
 * Returns the result
 * @param ctx the context object holds contextual information about the function invocation.
 */
export function response(ctx) {
	const { error, result } = ctx
	if (error) {
		return util.appendError(error.message, error.type, result)
	}
	return ctx.result
}
```

如果在请求期间出现错误，则该函数的响应处理程序附加一个错误，该错误将在 GraphQL 响应中返回到调用客户端。将 `GET_ITEM` 函数添加到您的解析器函数列表中。执行查询时，`GET_ITEM`函数的请求处理程序使用的 Dynam AWS AppSync oDB 模块提供的实用程序以`DynamoDBGetItem`作为密钥创建请求。`id` `ddb.get({ key: { id } })`生成相应的`GetItem`操作：

```
{
    "operation" : "GetItem",
    "key" : {
        "id" : { "S" : "1" }
    }
}
```

AWS AppSync 使用请求从亚马逊 DynamoDB 获取数据。在返回数据后，将由 `GET_ITEM` 函数的响应处理程序进行处理，响应处理程序检查错误，然后返回结果。

```
{
  "result" : {
    "id": 1,
    "title": "hello world",
    "content": "<long story>"
  }
}
```

最后，解析器的响应处理程序直接返回结果。

## 处理错误
<a name="working-with-errors"></a>

如果您的函数在请求期间出现错误，将在函数响应处理程序的 `ctx.error` 中提供该错误。您可以使用 `util.appendError` 实用程序将错误附加到 GraphQL 响应中。您可以使用存储区将错误提供给管道中的其他函数。请参见以下示例：

```
/**
 * Returns the result
 * @param ctx the context object holds contextual information about the function invocation.
 */
export function response(ctx) {
  const { error, result } = ctx;
  if (error) {
    if (!ctx.stash.errors) ctx.stash.errors = []
    ctx.stash.errors.push(ctx.error)
    return util.appendError(error.message, error.type, result);
  }
  return ctx.result;
}
```

# 为 `APPSYNC_JS` 运行时配置实用程序
<a name="utility-resolvers"></a>

AWS AppSync 提供了两个有助于使用`APPSYNC_JS`运行时开发解析器的库：
+ `@aws-appsync/eslint-plugin` - 在开发过程中快速捕获并修复问题。
+ `@aws-appsync/utils` - 在代码编辑器中提供类型验证和自动完成功能。

## 配置 ESLint 插件
<a name="utility-resolvers-configuring-eslint-plugin"></a>

[ESLint](https://eslint.org/)是一种静态分析代码以快速发现问题的工具。您可以 ESLint 作为持续集成管道的一部分运行。 `@aws-appsync/eslint-plugin`是一个 ESLint 插件，可在利用`APPSYNC_JS`运行时时时捕获代码中的无效语法。通过使用该插件，您可以在开发过程中快速获得有关代码的反馈，而无需将更改推送到云端。

`@aws-appsync/eslint-plugin` 提供了两个可以在开发过程中使用的规则集。

**"plugin:@aws-appsync/base"** 配置您可以在项目中使用的一组基本规则：


| 规则 | 说明 | 
| --- | --- | 
| no-async | 不支持异步过程和 Promise。 | 
| no-await | 不支持异步过程和 Promise。 | 
| no-classes | 不支持类。 | 
| no-for | 不支持 for（支持的 for-in 和 for-of 除外） | 
| no-continue | 不支持 continue。 | 
| no-generators | 不支持生成器。 | 
| no-yield | 不支持 yield。 | 
| no-labels | 不支持标签。 | 
| no-this | 不支持 this 关键字。 | 
| no-try | 不支持 try/catch 结构。 | 
| no-while | 不支持 while 循环。 | 
| no-disallowed-unary-operators | 不允许使用 \$1\$1、-- 和 \$1 一元运算符。 | 
| no-disallowed-binary-operators | 不允许使用 instanceof 运算符。 | 
| no-promise | 不支持异步过程和 Promise。 | 

**“插件：@aws-appsync/推荐”** 提供了一些额外的规则，但也要求你为项目添加 TypeScript 配置。


| 规则 | 说明 | 
| --- | --- | 
| no-recursion | 不允许使用递归函数调用。 | 
| no-disallowed-methods | 不允许使用某些方法。请参阅[参考](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-util-reference-js.html)以了解支持的全套内置函数。 | 
| no-function-passing | 不允许将函数作为函数参数传递给函数。 | 
| no-function-reassign | 无法重新分配函数。 | 
| no-function-return | 函数不能是函数的返回值。 | 

要将插件添加到您的项目中，请按照 “[入门” 中的安装和使用步骤进行](https://eslint.org/docs/latest/user-guide/getting-started#installation-and-usage)操作 ESLint。然后，使用项目包管理器（例如 npm、yarn 或 pnpm）在项目中安装[插件](https://www.npmjs.com/package/@aws-appsync/eslint-plugin)：

```
$ npm install @aws-appsync/eslint-plugin
```

在 `.eslintrc.{js,yml,json}` 文件中，将 **"plugin:@aws-appsync/base"** 或 **"plugin:@aws-appsync/recommended"** 添加到 `extends` 属性中。以下代码段是以下内容的基本示例`.eslintrc`配置： JavaScript

```
{
  "extends": ["plugin:@aws-appsync/base"]
}
```

要使用 **"plugin:@aws-appsync/recommended"** 规则集，请安装所需的依赖项：

```
$ npm install -D @typescript-eslint/parser
```

然后，创建一个 `.eslintrc.js` 文件：

```
{
  "parser": "@typescript-eslint/parser",
  "parserOptions": {
    "ecmaVersion": 2018,
    "project": "./tsconfig.json"
  },
  "extends": ["plugin:@aws-appsync/recommended"]
}
```

# 为运行时打包 TypeScript、和源映射 `APPSYNC_JS`
<a name="additional-utilities"></a>

TypeScript 通过提供类型安全和早期错误检测来增强 AWS AppSync 开发。你可以在本地编写 TypeScript 代码，然后将其转换为， JavaScript 然后再将其与`APPSYNC_JS`运行时一起使用。该过程从为环境安装 TypeScript 和配置 tsconfig.json 开始。`APPSYNC_JS`然后，您可以使用 esbuild 等捆绑工具编译和捆绑代码。Amplify CLI 将从 GraphQL 架构生成类型，您可以在解析器代码中使用这些类型。

您可以在解析器和函数代码中使用自定义库和外部库，只要这些库符合 `APPSYNC_JS` 要求即可。捆绑工具将代码合并到单个文件中以供在中使用。 AWS AppSync可以包含源映射，以帮助调试。

## 使用库并捆绑您的代码
<a name="using-external-libraries"></a>

在您的解析器和函数代码中，您可以使用自定义库和外部库，只要它们符合 `APPSYNC_JS` 要求即可。这样，就可以在应用程序中重复使用现有的代码。要使用由多个文件定义的库，必须使用捆绑工具（例如 [esbuild](https://esbuild.github.io/)）将代码合并到一个文件中，然后将其保存到 AWS AppSync 解析器或函数中。

在捆绑代码时，请记住以下几点：
+ `APPSYNC_JS`仅支持 ECMAScript 模块 (ESM)。
+ `@aws-appsync/*` 模块集成到 `APPSYNC_JS` 中，不应将其与您的代码捆绑在一起。
+ `APPSYNC_JS` 运行时系统与 NodeJS 类似，即，不会在浏览器环境中运行代码。
+ 您可以包含可选的源映射。不过，不要包含源内容。

  要了解源映射的更多信息，请参阅[使用源映射](#source-maps)。

例如，要捆绑位于 `src/appsync/getPost.resolver.js` 中的解析器代码，您可以使用以下 esbuild CLI 命令：

```
$ esbuild --bundle \
--sourcemap=inline \
--sources-content=false \
--target=esnext \
--platform=node \
--format=esm \
--external:@aws-appsync/utils \
--outdir=out/appsync \
 src/appsync/getPost.resolver.js
```

## 构建您的代码并使用 TypeScript
<a name="working-with-typescript"></a>

[TypeScript](https://www.typescriptlang.org/)是一种由 Microsoft 开发 JavaScript的编程语言，它提供所有功能以及 TypeScript 打字系统。在将代码保存 TypeScript 到之前，您可以使用编写类型安全的代码并在构建时捕获错误和错误。 AWS AppSync`@aws-appsync/utils` 包是完全类型化的。

`APPSYNC_JS`运行时不 TypeScript 直接支持。在将 TypeScript 代码保存到之前，必须先将 JavaScript 代码转换为`APPSYNC_JS`运行时支持的代码。 AWS AppSync您可以使用 TypeScript 在本地集成开发环境 (IDE) 中编写代码，但请注意，您无法在 AWS AppSync 控制台中创建 TypeScript 代码。

首先，请确保已在项目中[TypeScript](https://www.typescriptlang.org/download)安装。然后，使用配置您的 TypeScript 转码设置以与`APPSYNC_JS`运行时配合使用[TSConfig](https://www.typescriptlang.org/tsconfig)。以下是您可以使用的基本 `tsconfig.json` 文件示例：

```
// tsconfig.json
{
  "compilerOptions": {
    "target": "esnext",
    "module": "esnext",
   "noEmit": true,
   "moduleResolution": "node",
  }
}
```

然后，您可以使用 esbuild 等捆绑工具编译和捆绑代码。例如，给定一个项目，你的 AWS AppSync 代码位于`src/appsync`，你可以使用以下命令来编译和捆绑你的代码：

```
$ esbuild --bundle \
--sourcemap=inline \
--sources-content=false \
--target=esnext \
--platform=node \
--format=esm \
--external:@aws-appsync/utils \
--outdir=out/appsync \
 src/appsync/**/*.ts
```

### 使用 Amplify codegen
<a name="working-with-amplify-codegen"></a>

您可以使用 [Amplify CLI](https://docs.amplify.aws/cli/) 生成架构的类型。从 `schema.graphql` 文件所在的目录中，运行以下命令并查看提示以配置 codegen：

```
$  npx @aws-amplify/cli codegen add
```

要在某些情况下（例如，更新架构时）重新生成 codegen，请运行以下命令：

```
$ npx @aws-amplify/cli codegen
```

然后，您可以在解析器代码中使用生成的类型。例如，给定以下架构：

```
type Todo {
  id: ID!
  title: String!
  description: String
}

type Mutation {
  createTodo(title: String!, description: String): Todo
}

type Query {
  listTodos: Todo
}
```

你可以在以下示例 AWS AppSync 函数中使用生成的类型：

```
import { Context, util } from '@aws-appsync/utils'
import * as ddb from '@aws-appsync/utils/dynamodb'
import { CreateTodoMutationVariables, Todo } from './API' // codegen

export function request(ctx: Context<CreateTodoMutationVariables>) {
	ctx.args.description = ctx.args.description ?? 'created on ' + util.time.nowISO8601()
	return ddb.put<Todo>({ key: { id: util.autoId() }, item: ctx.args })
}

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

### 在中使用泛型 TypeScript
<a name="working-with-typescript-generics"></a>

您可以将泛型与提供的多种类型一起使用。例如，下面的代码片段是 `Todo` 类型：

```
export type Todo = {
  __typename: "Todo",
  id: string,
  title: string,
  description?: string | null,
};
```

您可以为使用 `Todo` 的订阅编写解析器。在您的 IDE 中，类型定义和自动完成提示将指导您正确使用 `toSubscriptionFilter` 转换实用程序：

```
import { util, Context, extensions } from '@aws-appsync/utils'
import { Todo } from './API'

export function request(ctx: Context) {
  return {}
}

export function response(ctx: Context) {
  const filter = util.transform.toSubscriptionFilter<Todo>({
    title: { beginsWith: 'hello' },
    description: { contains: 'created' },
  })
  extensions.setSubscriptionFilter(filter)
  return null
}
```

## 检查您的包
<a name="using-lint-with-bundles"></a>

您可以导入 `esbuild-plugin-eslint` 插件以自动检查您的包。然后，您可以提供启用 ESLint 功能的 `plugins` 值以启用该插件。以下是在名为的文件中使用 esbuild JavaScript API 的片段：`build.mjs`

```
/* eslint-disable */
import { build } from 'esbuild'
import eslint from 'esbuild-plugin-eslint'
import glob from 'glob'
const files = await glob('src/**/*.ts')

await build({
  format: 'esm',
  target: 'esnext',
  platform: 'node',
  external: ['@aws-appsync/utils'],
  outdir: 'dist/',
  entryPoints: files,
  bundle: true,
  plugins: [eslint({ useEslintrc: true })],
})
```

## 使用源映射
<a name="source-maps"></a>

您可以在 JavaScript 代码中提供内联源映射 (`sourcemap`)。当您打包 JavaScript 或 TypeScript 编写代码并希望在日志和运行时 JavaScript 错误消息中查看对输入源文件的引用时，源映射非常有用。

您的 `sourcemap` 必须出现在代码末尾。它是由采用以下格式的单个注释行定义的：

```
//# sourceMappingURL=data:application/json;base64,<base64 encoded string>
```

示例如下：

```
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsibGliLmpzIiwgImNvZGUuanMiXSwKICAibWFwcGluZ3MiOiAiO0FBQU8sU0FBUyxRQUFRO0FBQ3RCLFNBQU87QUFDVDs7O0FDRE8sU0FBUyxRQUFRLEtBQUs7QUFDM0IsU0FBTyxNQUFNO0FBQ2Y7IiwKICAibmFtZXMiOiBbXQp9Cg==
```

可以使用 esbuild 创建源映射。以下示例向您展示了在构建和捆绑代码时如何使用 esbuild JavaScript API 来包含内联源映射：

```
/* eslint-disable */
import { build } from 'esbuild'
import eslint from 'esbuild-plugin-eslint'
import glob from 'glob'
const files = await glob('src/**/*.ts')

await build({
  sourcemap: 'inline',
  sourcesContent: false,
  
  format: 'esm',
  target: 'esnext',
  platform: 'node',
  external: ['@aws-appsync/utils'],
  outdir: 'dist/',
  entryPoints: files,
  bundle: true,
  plugins: [eslint({ useEslintrc: true })],
})
```

特别是，`sourcemap` 和 `sourcesContent` 选项指定应在每个构建末尾的行中添加源映射，但源映射不应包含源内容。作为惯例，我们建议不要在您的 `sourcemap` 中包含源内容。您可以将 `sources-content` 设置为 `false` 以在 esbuild 中禁用该功能。

要说明源映射的工作方式，请查看以下示例，其中解析器代码引用帮助程序库中的帮助程序函数。该代码在解析器代码和帮助程序库中包含日志语句：

**./src/default.resolver.ts**（您的解析器）

```
import { Context } from '@aws-appsync/utils'
import { hello, logit } from './helper'

export function request(ctx: Context) {
  console.log('start >')
  logit('hello world', 42, true)
  console.log('< end')
  return 'test'
}

export function response(ctx: Context): boolean {
  hello()
  return ctx.prev.result
}
```

**.src/helper.ts**（帮助程序文件）

```
export const logit = (...rest: any[]) => {
  // a special logger
  console.log('[logger]', ...rest.map((r) => `<${r}>`))
}

export const hello = () => {
  // This just returns a simple sentence, but it could do more.
  console.log('i just say hello..')
}
```

在您构建并捆绑解析器文件时，您的解析器代码将包含内联源映射。当您的解析器运行时， CloudWatch 日志中会显示以下条目：

![\[CloudWatch log entries showing resolver code execution with inline source map information.\]](http://docs.aws.amazon.com/zh_cn/appsync/latest/devguide/images/cloudwatch-sourcemap.jpeg)


查看 CloudWatch 日志中的条目，您会注意到这两个文件的功能已捆绑在一起并行运行。每个文件的原始文件名也清晰地反映在日志中。

# 在中测试你的解析器和函数处理器 AWS AppSync
<a name="test-resolvers"></a>

在将代码保存到解析器或函数之前，您可以通过 `EvaluateCode` API 命令使用模拟数据远程测试解析器和函数处理程序。要开始使用该命令，请确保您已将 `appsync:evaluatecode` 权限添加到您的策略中。例如：

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

****  

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

------

您可以使用 [AWS CLI](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/appsync/index.html) 或来利用该命令[AWS SDKs](https://aws.amazon.com/tools/)。例如，要使用 CLI 测试代码，只需指向您的文件，提供上下文并指定要评估的处理程序：

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

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

```
{
    "evaluationResult": "{\"operation\":\"PutItem\",\"key\":{\"id\":{\"S\":\"record-id\"}},\"attributeValues\":{\"owner\":{\"S\":\"John doe\"},\"expectedVersion\":{\"N\":2},\"authorId\":{\"S\":\"Sammy Davis\"}}}",
    "logs": [
        "INFO - code.js:5:3: \"current id\" \"record-id\"",
        "INFO - code.js:9:3: \"request evaluated\""
    ]
}
```

可以将评估结果解析为 JSON，其中提供：

```
{
  "operation": "PutItem",
  "key": {
    "id": {
      "S": "record-id"
    }
  },
  "attributeValues": {
    "owner": {
      "S": "John doe"
    },
    "expectedVersion": {
      "N": 2
    },
    "authorId": {
      "S": "Sammy Davis"
    }
  }
}
```

通过使用 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
```

# 从 VTL 迁移到 in JavaScript AWS AppSync
<a name="migrating-resolvers"></a>

AWS AppSync 允许您使用 VTL 或为解析器和函数编写业务逻辑。 JavaScript使用这两种语言，您都可以编写逻辑来指导 AWS AppSync 服务如何与您的数据源进行交互。在使用 VTL 时，您可以编写评估结果必须为有效 JSON 编码字符串的映射模板。使用 JavaScript，您可以编写返回对象的请求和响应处理程序。您不会返回 JSON 编码的字符串。

例如，采用以下 VTL 映射模板以获取 Amazon DynamoDB 项目：

```
{
    "operation": "GetItem",
    "key": {
        "id": $util.dynamodb.toDynamoDBJson($ctx.args.id),
    }
}
```

`$util.dynamodb.toDynamoDBJson` 实用程序返回 JSON 编码的字符串。如果 `$ctx.args.id` 设置为 `<id>`，则模板评估结果为有效的 JSON 编码字符串：

```
{
    "operation": "GetItem",
    "key": {
        "id": {"S": "<id>"},
    }
}
```

使用时 JavaScript，您无需在代码中打印出未经处理的 JSON 编码字符串，`toDynamoDBJson`也不需要使用类似的实用程序。上述映射模板的等效示例是：

```
import { util } from '@aws-appsync/utils';
export function request(ctx) {
  return {
    operation: 'GetItem',
    key: {id: util.dynamodb.toDynamoDB(ctx.args.id)}
  };
}
```

另一种方法是使用 `util.dynamodb.toMapValues`，这是处理值对象的建议方法：

```
import { util } from '@aws-appsync/utils';
export function request(ctx) {
  return {
    operation: 'GetItem',
    key: util.dynamodb.toMapValues({ id: ctx.args.id }),
  };
}
```

它的评估结果为：

```
{
  "operation": "GetItem",
  "key": {
    "id": {
      "S": "<id>"
    }
  }
}
```

**注意**  
我们建议将 DynamoDB 模块与 DynamoDB 数据来源一起使用：  

```
import * as ddb from '@aws-appsync/utils/dynamodb'

export function request(ctx) {
	ddb.get({ key: { id: ctx.args.id } })
}
```

再举一个例子，采用以下映射模板将项目放入 Amazon DynamoDB 数据来源中：

```
{
    "operation" : "PutItem",
    "key" : {
        "id": $util.dynamodb.toDynamoDBJson($util.autoId()),
    },
    "attributeValues" : $util.dynamodb.toMapValuesJson($ctx.args)
}
```

在评估后，该映射模板字符串必须生成有效的 JSON 编码字符串。使用时 JavaScript，您的代码会直接返回请求对象：

```
import { util } from '@aws-appsync/utils';
export function request(ctx) {
  const { id = util.autoId(), ...values } = ctx.args;
  return {
    operation: 'PutItem',
    key: util.dynamodb.toMapValues({ id }),
    attributeValues: util.dynamodb.toMapValues(values),
  };
}
```

它的评估结果为：

```
{
  "operation": "PutItem",
  "key": {
    "id": { "S": "2bff3f05-ff8c-4ed8-92b4-767e29fc4e63" }
  },
  "attributeValues": {
    "firstname": { "S": "Shaggy" },
    "age": { "N": 4 }
  }
}
```

**注意**  
我们建议将 DynamoDB 模块与 DynamoDB 数据来源一起使用：  

```
import { util } from '@aws-appsync/utils'
import * as ddb from '@aws-appsync/utils/dynamodb'

export function request(ctx) {
	const { id = util.autoId(), ...item } = ctx.args
	return ddb.put({ key: { id }, item })
}
```

# 在直接数据来源访问和通过 Lambda 数据来源代理之间进行选择
<a name="choosing-data-source"></a>

借 AWS AppSync 助 and `APPSYNC_JS` the runtime，您可以编写自己的代码，通过使用 AWS AppSync 函数访问您的数据源来实现您的自定义业务逻辑。这使您可以轻松地直接与 Amazon DynamoDB、Aurora OpenSearch Serverless、服务 APIs、HTTP 和其他服务等数据源进行交互， AWS 而无需部署额外的计算服务或基础设施。 AWS AppSync 还可以通过配置 Lambda 数据源轻松地与 AWS Lambda 函数进行交互。Lambda 数据源允许您使用全套功能运行复杂 AWS Lambda的业务逻辑来解析 GraphQL 请求。在大多数情况下，直接连接到其目标数据源的 AWS AppSync 函数将提供您需要的所有功能。在您需要实施 `APPSYNC_JS` 运行时系统不支持的复杂业务逻辑时，您可以将 Lambda 数据来源作为代理以与目标数据来源进行交互。


|  |  |  | 
| --- |--- |--- |
|  | 直接数据源集成 | 作为代理的 Lambda 数据源 | 
| 使用案例 | AWS AppSync 函数直接与 API 数据源交互。 | AWS AppSync 函数调用与 API 数据源交互的 Lambda。 | 
| 运行时 | APPSYNC\$1JS (JavaScript) | 任何支持的 Lambda 运行时 | 
| 代码的最大大小 | 每个函数 32,000 个 AWS AppSync字符 | 每个 Lambda 50 MB（已压缩，用于直接上传） | 
| 外部模块 | 有限——仅支持 APPSYNC\$1JS 的功能 | 是 | 
| 拨打任何 AWS 服务电话 | 是-使用 AWS AppSync HTTP 数据源 | 是-使用 AWS SDK | 
| 访问请求标头 | 是 | 是 | 
| 网络访问 | 否 | 是 | 
| 文件系统访问 | 否 | 是 | 
| 日志和指标 | 是 | 是 | 
| 完全在内部构建和测试 AppSync | 是 | 否 | 
| 冷启动 | 否 | 否-使用预配置的并发性 | 
| 自动扩缩 | 是的——透明地 AWS AppSync | 是-按照 Lambda 中的配置 | 
| 定价 | 不收取额外费用 | 针对 Lambda 使用量收费 | 

AWS AppSync 直接与其目标数据源集成的函数非常适合以下用例：
+  与亚马逊 DynamoDB、Aurora Serverless 和服务互动 OpenSearch 
+  与 HTTP 交互 APIs 并传递传入的标头 
+  使用 HTTP 数据源与 AWS 服务交互（使用提供的数据源角色 AWS AppSync 自动签署请求） 
+  在访问数据来源之前实施访问控制 
+  在完成请求之前对检索的数据实施筛选 
+  通过在解析器管道中按顺序执行 AWS AppSync 函数来实现简单的编排 
+  控制查询和变更中的缓存和订阅连接。

AWS AppSync 使用 Lambda 数据源作为代理的函数非常适合以下用例：
+  使用 Velocity 模板语言 (VTL) 以外的 JavaScript 语言 
+  调整和控制 CPU 或内存以优化性能 
+  导入第三方库或要求使用 `APPSYNC_JS` 中不支持的功能 
+  发出多个网络请求以 and/or 获取文件系统访问权限以完成查询 
+  使用[批处理配置](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-reference-lambda-js.html)对请求进行批处理。