教程:组合使用 GraphQL 解析器 - Amazon AppSync
Amazon Web Services 文档中描述的 Amazon Web Services 服务或功能可能因区域而异。要查看适用于中国区域的差异,请参阅 中国的 Amazon Web Services 服务入门 (PDF)

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

教程:组合使用 GraphQL 解析器

注意

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

GraphQL 架构中的解析器和字段具有 1:1 的关系,具有很高的灵活性。由于数据源是独立于架构在解析器上配置的,因此您可以通过不同的数据源解析和操控 GraphQL 类型,同时在架构上混合和匹配数据源以最好地满足您的需要。

以下示例场景说明了如何在架构中混合使用和匹配数据源。在开始之前,我们建议您熟悉如何设置数据源以及 Amazon Lambda、Amazon DynamoDB 和 Amazon OpenSearch Service 的解析器,如前面的教程中所述。

示例架构

以下架构具有一个名为 Post 的类型,它定义了 3 个 Query 操作和 3 个 Mutation 操作:

type Post { id: ID! author: String! title: String content: String url: String ups: Int downs: Int version: Int! } type Query { allPost: [Post] getPost(id: ID!): Post searchPosts: [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 }

在本示例中,您共有 6 个可附加的解析器。一种可能的方法是,让所有这些解析器来自名为 Posts 的 Amazon DynamoDB 表,其中 AllPosts 运行扫描,searchPosts 运行查询,如 DynamoDB 解析器映射模板参考中所述。不过,可以使用替代方法满足您的业务需求,例如从 Lambda 或 OpenSearch Service 中解析这些 GraphQL 查询。

通过解析器更改数据

您可能需要将结果从数据库(例如 DynamoDB 或 Amazon Aurora)返回到客户端,并更改某些属性。这可能是由于数据类型的格式设置所致(例如,客户端上的时间戳差异),或者是为了处理向后兼容性问题。出于说明目的,在以下示例中,每次调用 GraphQL 解析器时,Amazon Lambda 函数为其分配随机数以处理博客文章的点赞和差评操作:

'use strict'; const doc = require('dynamodb-doc'); const dynamo = new doc.DynamoDB(); exports.handler = (event, context, callback) => { const payload = { TableName: 'Posts', Limit: 50, Select: 'ALL_ATTRIBUTES', }; dynamo.scan(payload, (err, data) => { const result = { data: data.Items.map(item =>{ item.ups = parseInt(Math.random() * (50 - 10) + 10, 10); item.downs = parseInt(Math.random() * (20 - 0) + 0, 10); return item; }) }; callback(err, result.data); }); };

这是一个完全有效的 Lambda 函数,可以附加到 GraphQL 架构中的 AllPosts 字段,以便返回所有结果的任何查询都可以对顶/踩操作获得随机数字。

DynamoDB 和 OpenSearch Service

对于某些应用程序,您可能会对 DynamoDB 执行变更或简单查找查询,并让后台进程将文档传输到 OpenSearch Service。然后,您可以简单地将 searchPosts 解析器附加到 OpenSearch Service 数据源,并使用 GraphQL 查询返回搜索结果(从源自 DynamoDB 的数据)。在应用程序中添加高级搜索操作(例如关键字、模糊字词匹配,甚至地理空间查找)时,这可能是非常强大的。可以通过 ETL 流程完成从 DynamoDB 传输数据的过程,也可以使用 Lambda 从 DynamoDB 进行流式传输。可以在您的 Amazon 账户的 US West 2(俄勒冈州)区域中通过以下 Amazon CloudFormation 堆栈启动完整的示例:

该示例中的架构允许您使用 DynamoDB 解析器添加文章,如下所示:

mutation add { putPost(author:"Nadia" title:"My first post" content:"This is some test content" url:"https://aws.amazon.com/appsync/" ){ id title } }

这会将数据写入到 DynamoDB 中,然后 DynamoDB 通过 Lambda 将数据流式传输到 Amazon OpenSearch Service,您可以使用该服务按不同字段搜索文章。例如,由于数据位于 Amazon OpenSearch Service 中,您可以使用自由格式文本(甚至包含空格)搜索作者或内容字段,如下所示:

query searchName{ searchAuthor(name:" Nadia "){ id title content } } query searchContent{ searchContent(text:"test"){ id title content } }

由于数据直接写入到 DynamoDB 中,因此,您仍然可以使用 allPosts{...}singlePost{...} 查询对表执行高效的列表或项目查找操作。该堆栈将以下示例代码用于 DynamoDB 流:

注意:此代码仅用于举例说明。

var AWS = require('aws-sdk'); var path = require('path'); var stream = require('stream'); var esDomain = { endpoint: 'https://opensearch-domain-name.REGION.es.amazonaws.com', region: 'REGION', index: 'id', doctype: 'post' }; var endpoint = new AWS.Endpoint(esDomain.endpoint) var creds = new AWS.EnvironmentCredentials('AWS'); function postDocumentToES(doc, context) { var req = new AWS.HttpRequest(endpoint); req.method = 'POST'; req.path = '/_bulk'; req.region = esDomain.region; req.body = doc; req.headers['presigned-expires'] = false; req.headers['Host'] = endpoint.host; // Sign the request (Sigv4) var signer = new AWS.Signers.V4(req, 'es'); signer.addAuthorization(creds, new Date()); // Post document to ES var send = new AWS.NodeHttpClient(); send.handleRequest(req, null, function (httpResp) { var body = ''; httpResp.on('data', function (chunk) { body += chunk; }); httpResp.on('end', function (chunk) { console.log('Successful', body); context.succeed(); }); }, function (err) { console.log('Error: ' + err); context.fail(); }); } exports.handler = (event, context, callback) => { console.log("event => " + JSON.stringify(event)); var posts = ''; for (var i = 0; i < event.Records.length; i++) { var eventName = event.Records[i].eventName; var actionType = ''; var image; var noDoc = false; switch (eventName) { case 'INSERT': actionType = 'create'; image = event.Records[i].dynamodb.NewImage; break; case 'MODIFY': actionType = 'update'; image = event.Records[i].dynamodb.NewImage; break; case 'REMOVE': actionType = 'delete'; image = event.Records[i].dynamodb.OldImage; noDoc = true; break; } if (typeof image !== "undefined") { var postData = {}; for (var key in image) { if (image.hasOwnProperty(key)) { if (key === 'postId') { postData['id'] = image[key].S; } else { var val = image[key]; if (val.hasOwnProperty('S')) { postData[key] = val.S; } else if (val.hasOwnProperty('N')) { postData[key] = val.N; } } } } var action = {}; action[actionType] = {}; action[actionType]._index = 'id'; action[actionType]._type = 'post'; action[actionType]._id = postData['id']; posts += [ JSON.stringify(action), ].concat(noDoc?[]:[JSON.stringify(postData)]).join('\n') + '\n'; } } console.log('posts:',posts); postDocumentToES(posts, context); };

然后,您可以使用 DynamoDB 流将其附加到主键为 id 的 DynamoDB 表,并将对 DynamoDB 源的任何更改流式传输到您的 OpenSearch Service 域。有关配置上述功能的更多信息,请参阅 DynamoDB 流文档