Lambda 的解析器映射模板参考 - Amazon AppSync
Amazon Web Services 文档中描述的 Amazon Web Services 服务或功能可能因区域而异。要查看适用于中国区域的差异,请参阅 中国的 Amazon Web Services 服务入门 (PDF)

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

Lambda 的解析器映射模板参考

注意

我们现在主要支持 APPSYNC_JS 运行时环境及其文档。请考虑使用 APPSYNC_JS 运行时环境和此处的指南。

您可以使用 Amazon Lambda 的 Amazon AppSync 解析器映射模板设置从 Amazon AppSync 到位于您的账户中的 Lambda 函数的请求形状,以及从您的 Lambda 函数到 Amazon AppSync 的响应形状。您也可以使用映射模板向 Amazon AppSync 提供提示,以指出要调用的操作的性质。本节介绍了支持的 Lambda 操作的各种映射模板。

请求映射模板

Lambda 请求映射模板相当简单,允许尽可能多的上下文信息传递到 Lambda 函数。

{ "version": string, "operation": Invoke|BatchInvoke, "payload": any type }

以下是解析的 Lambda 请求映射模板的 JSON 架构表示形式。

{ "definitions": {}, "$schema": "https://json-schema.org/draft-06/schema#", "$id": "https://aws.amazon.com/appsync/request-mapping-template.json", "type": "object", "properties": { "version": { "$id": "/properties/version", "type": "string", "enum": [ "2018-05-29" ], "title": "The Mapping template version.", "default": "2018-05-29" }, "operation": { "$id": "/properties/operation", "type": "string", "enum": [ "Invoke", "BatchInvoke" ], "title": "The Mapping template operation.", "description": "What operation to execute.", "default": "Invoke" }, "payload": {} }, "required": [ "version", "operation" ], "additionalProperties": false }

以下是一个示例,我们从上下文中传递 field 值和 GraphQL 字段参数。

{ "version": "2018-05-29", "operation": "Invoke", "payload": { "field": "getPost", "arguments": $util.toJson($context.arguments) } }

整个映射文档作为输入传递给您的 Lambda 函数,因此,上一示例现在如下所示:

{ "version": "2018-05-29", "operation": "Invoke", "payload": { "field": "getPost", "arguments": { "id": "postId1" } } }

版本

version 定义模板使用的版本,这一点对于所有请求映射模板都相同。version 是必需的。

"version": "2018-05-29"

操作

Lambda 数据源允许您定义两个操作:InvokeBatchInvokeInvoke 操作让 Amazon AppSync 知道为每个 GraphQL 字段解析器调用您的 Lambda 函数。BatchInvoke 指示 Amazon AppSync 批处理当前 GraphQL 字段的请求。

operation 是必需的。

对于 Invoke,解析的请求映射模板与 Lambda 函数的输入负载完全匹配。因此,以下示例模板:

{ "version": "2018-05-29", "operation": "Invoke", "payload": { "arguments": $util.toJson($context.arguments) } }

经过解析并传递给 Lambda 函数,如下所示:

{ "version": "2018-05-29", "operation": "Invoke", "payload": { "arguments": { "id": "postId1" } } }

对于 BatchInvoke,映射模板应用于批次中的每个字段解析器。为了简洁起见,Amazon AppSync 将所有解析的映射模板 payload 值合并到与映射模板匹配的单个对象中的列表。

以下示例模板显示合并:

{ "version": "2018-05-29", "operation": "BatchInvoke", "payload": $util.toJson($context) }

此模板解析为以下映射文档:

{ "version": "2018-05-29", "operation": "BatchInvoke", "payload": [ {...}, // context for batch item 1 {...}, // context for batch item 2 {...} // context for batch item 3 ] }

其中,payload 列表的每个元素对应于一个批处理项目。Lambda 函数也预期返回列表形状的响应,并与请求中发送的项目顺序匹配,如下所示:

[ { "data": {...}, "errorMessage": null, "errorType": null }, // result for batch item 1 { "data": {...}, "errorMessage": null, "errorType": null }, // result for batch item 2 { "data": {...}, "errorMessage": null, "errorType": null } // result for batch item 3 ]

operation 是必需的。

有效负载

payload 字段是一个容器,可用于将任何格式正确的 JSON 传递到 Lambda 函数。

如果 operation 字段设置为 BatchInvoke,则 Amazon AppSync 将现有 payload 值包装到一个列表中。

payload 为可选项。

响应映射模板

与其他数据源一样,您的 Lambda 函数向 Amazon AppSync 发送响应,该响应必须转换为 GraphQL 类型。

Lambda 函数的结果是在通过 Velocity 模板语言 (VTL) $context.result 属性提供的 context 对象上设置的。

如果 Lambda 函数响应的形状与 GraphQL 类型的形状完全匹配,您可以使用以下响应映射模板转发响应:

$util.toJson($context.result)

没有必填字段,也没有形状限制应用于响应映射模板。但是,由于 GraphQL 是强类型化的,因此解析的映射模板必须与预期的 GraphQL 类型匹配。

Lambda 函数批处理的响应

如果 operation 字段设置为 BatchInvoke,Amazon AppSync 需要从 Lambda 函数返回一个项目列表。要让 Amazon AppSync 将每个结果映射回原始请求项目,响应列表的大小和顺序必须匹配。可以在响应列表中包含 null 项目;$ctx.result 相应地设置为 null

直接 Lambda 解析器

如果您希望完全避免使用映射模板,Amazon AppSync 可以为您的 Lambda 函数提供默认负载以及从 Lambda 函数响应到 GraphQL 类型的默认转换。您可以选择提供请求模板、响应模板或两者都不提供,Amazon AppSync 将相应地进行处理。

直接 Lambda 请求映射模板

如果未提供请求映射模板,则 Amazon AppSync 将 Context 对象作为 Invoke 操作直接发送到您的 Lambda 函数。有关 Context 对象结构的更多信息,请参阅 解析器映射模板上下文参考

直接 Lambda 响应映射模板

如果未提供响应映射模板,则 Amazon AppSync 在收到 Lambda 函数响应后执行以下两个操作之一。如果未提供请求映射模板,或者提供了版本为“2018-05-29”的请求映射模板,则响应逻辑功能相当于以下响应映射模板:

#if($ctx.error) $util.error($ctx.error.message, $ctx.error.type, $ctx.result) #end $util.toJson($ctx.result)

如果您提供版本为“2017-02-28”的模板,则响应逻辑功能相当于以下响应映射模板:

$util.toJson($ctx.result)

从表面上看,映射模板绕过功能与使用某些映射模板类似,如前面的示例中所示。但在幕后,完全绕过了映射模板评估。由于绕过了模板评估步骤,在某些场景中,与具有需要评估的响应映射模板的 Lambda 函数相比,应用程序在响应期间可能会减少开销和延迟。

直接 Lambda 解析器响应中的自定义错误处理

您可以通过引发自定义异常,自定义直接 Lambda 解析器调用的 Lambda 函数的错误响应。以下示例说明了如何使用 JavaScript 创建自定义异常:

class CustomException extends Error { constructor(message) { super(message); this.name = "CustomException"; } } throw new CustomException("Custom message");

在引发异常时,errorTypeerrorMessage 分别是引发的自定义错误的 namemessage

如果 errorTypeUnauthorizedException,则 Amazon AppSync 返回默认消息 ("You are not authorized to make this call."),而不是自定义消息。

以下是一个说明自定义 errorType 的示例 GraphQL 响应。

{ "data": { "query": null }, "errors": [ { "path": [ "query" ], "data": null, "errorType": "CustomException", "errorInfo": null, "locations": [ { "line": 5, "column": 10, "sourceName": null } ], "message": "Custom Message" } ] }

直接 Lambda 解析器:已启用批处理

您可以通过在解析器上配置 maxBatchSize,为直接 Lambda 解析器启用批处理。在直接 Lambda 解析器的 maxBatchSize 设置为大于 0 的值时,Amazon AppSync 向您的 Lambda 函数批量发送大小最多为 maxBatchSize 的请求。

如果在直接 Lambda 解析器上将 maxBatchSize 设置为 0,将关闭批处理。

有关 Lambda 解析器上的批处理的工作方式的更多信息,请参阅高级使用案例:批处理

请求映射模板

如果启用了批处理并且未提供请求映射模板,则 Amazon AppSync 将 Context 对象列表作为 BatchInvoke 操作直接发送到您的 Lambda 函数。

响应映射模板

如果启用了批处理并且未提供响应映射模板,响应逻辑相当于以下响应映射模板:

#if( $context.result && $context.result.errorMessage ) $utils.error($context.result.errorMessage, $context.result.errorType, $context.result.data) #else $utils.toJson($context.result.data) #end

Lambda 函数返回结果列表的顺序必须与发送的 Context 对象列表相同。您可以为特定结果提供 errorMessageerrorType 以返回各个错误。列表中的每个结果采用以下格式:

{ "data" : { ... }, // your data "errorMessage" : { ... }, // optional, if included an error entry is added to the "errors" object in the AppSync response "errorType" : { ... } // optional, the error type }
注意

目前忽略结果对象中的其他字段。

处理来自 Lambda 的错误

您可以通过在 Lambda 函数中引发异常或错误,为所有结果返回错误。如果批处理请求的负载请求或响应太大,Lambda 将返回错误。在这种情况下,您应该考虑减少 maxBatchSize 或减少响应负载大小。

有关处理各个错误的信息,请参阅返回单个错误

示例 Lambda 函数

通过使用下面的架构,您可以为 Post.relatedPosts 字段解析器创建直接 Lambda 解析器,并将 maxBatchSize 设置为大于 0 以启用批处理:

schema { query: Query mutation: Mutation } type Query { getPost(id:ID!): Post allPosts: [Post] } type Mutation { addPost(id: ID!, author: String!, title: String, content: String, url: String): Post! } type Post { id: ID! author: String! title: String content: String url: String ups: Int downs: Int relatedPosts: [Post] }

在以下查询中,将为批量请求调用 Lambda 函数以解析 relatedPosts

query getAllPosts { allPosts { id relatedPosts { id } } }

下面提供了 Lambda 函数的简单实施:

const posts = { 1: { id: '1', title: 'First book', author: 'Author1', url: 'https://amazon.com/', content: 'SAMPLE TEXT AUTHOR 1 SAMPLE TEXT AUTHOR 1 SAMPLE TEXT AUTHOR 1 SAMPLE TEXT AUTHOR 1 SAMPLE TEXT AUTHOR 1 SAMPLE TEXT AUTHOR 1', ups: '100', downs: '10', }, 2: { id: '2', title: 'Second book', author: 'Author2', url: 'https://amazon.com', content: 'SAMPLE TEXT AUTHOR 2 SAMPLE TEXT AUTHOR 2 SAMPLE TEXT', ups: '100', downs: '10', }, 3: { id: '3', title: 'Third book', author: 'Author3', url: null, content: null, ups: null, downs: null }, 4: { id: '4', title: 'Fourth book', author: 'Author4', url: 'https://www.amazon.com/', content: 'SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4', ups: '1000', downs: '0', }, 5: { id: '5', title: 'Fifth book', author: 'Author5', url: 'https://www.amazon.com/', content: 'SAMPLE TEXT AUTHOR 5 SAMPLE TEXT AUTHOR 5 SAMPLE TEXT AUTHOR 5 SAMPLE TEXT AUTHOR 5 SAMPLE TEXT', ups: '50', downs: '0', }, } const relatedPosts = { 1: [posts['4']], 2: [posts['3'], posts['5']], 3: [posts['2'], posts['1']], 4: [posts['2'], posts['1']], 5: [], } exports.handler = async (event) => { console.log('event ->', event) // retrieve the ID of each post const ids = event.map((context) => context.source.id) // fetch the related posts for each post id const related = ids.map((id) => relatedPosts[id]) // return the related posts; or an error if none were found return related.map((r) => { if (r.length > 0) { return { data: r } } else { return { data: null, errorMessage: 'Not found', errorType: 'ERROR' } } }) }