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

适用于 Lambda 的 Amazon AppSync 解析器映射模板参考

注意

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

可以使用 Amazon AppSync 函数和解析器来调用位于您账户中的 Lambda 函数。您可以在将请求有效载荷和 Lambda 函数响应返回给客户端之前对其进行调整。您也可以使用映射模板向 Amazon AppSync 提供提示,以指出要调用的操作的性质。本节介绍了支持的 Lambda 操作的各种映射模板。

请求映射模板

Lambda 请求映射模板可以处理与您的 Lambda 函数相关的字段:

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

以下是解析的 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": {}, "invocationType": { "$id": "/properties/invocationType", "type": "string", "enum": [ "RequestResponse", "Event" ], "title": "The Mapping template invocation type.", "description": "What invocation type to execute.", "default": "RequestResponse" } }, "required": [ "version", "operation" ], "additionalProperties": false }

以下示例使用了一个 invoke 操作,其有效载荷数据是 GraphQL 架构中的 getPost 字段以及来自上下文的参数:

{ "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 数据来源允许您在 operation 字段中定义两个操作: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 ]

有效负载

payload 字段是一个容器,用于将任何格式正确的 JSON 传递到 Lambda 函数。如果 operation 字段设置为 BatchInvoke,则 Amazon AppSync 将现有 payload 值包装到一个列表中。payload 字段为可选项。

调用类型

Lambda 数据来源让您可以定义两个调用类型:RequestResponseEvent。这些调用类型等同于 Lambda API 中定义的调用类型。RequestResponse 调用类型让 Amazon AppSync 可以同步调用您的 Lambda 函数以等待响应。Event 调用让您可以异步调用 Lambda 函数。有关 Lambda 如何处理 Event 调用类型请求的更多信息,请参阅异步调用invocationType 字段为可选项。如果请求中未包含此字段,Amazon AppSync 将默认为 RequestResponse 调用类型。

对于任何 invocationType 字段,解析的请求与 Lambda 函数的输入有效载荷匹配。我们来修改上面的示例:

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

这已解析并传递给 Lambda 函数,可能如下所示:

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

BatchInvoke 操作与 Event 调用类型字段结合使用时,Amazon AppSync 会以上述相同的方式合并字段解析器,并将请求作为异步事件传递给您的 Lambda 函数,其中 payload 是一个值列表。建议您对 Event 调用类型解析器禁用解析器缓存,因为如果出现缓存命中,这些解析器不会发送到 Lambda。

响应映射模板

与其他数据来源一样,您的 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 对象结构的更多信息,请参阅 Amazon AppSync 解析器映射模板上下文参考

直接 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' } } }) }