教程:DynamoDB 解析程序 - AWS AppSync
AWS 文档中描述的 AWS 服务或功能可能因区域而异。要查看适用于中国区域的差异,请参阅中国的 AWS 服务入门

教程:DynamoDB 解析程序

本教程展示了如何在 AWS AppSync 中使用您自己的 Amazon DynamoDB 表,并将它们连接到 GraphQL API。

您可以让 AWS AppSync 代表您预配置 DynamoDB 资源。如果您愿意,也可以创建数据源和解析程序,将现有的表连接到 GraphQL 架构。在这两种情况下,您都可以通过 GraphQL 语句读写您的 DynamoDB 数据库,并订阅实时数据。

要将 GraphQL 语句转换为 DynamoDB 操作,并将响应转换回 GraphQL,需要完成一些特定的配置步骤。本教程通过一些现实世界的场景和数据访问模式介绍了配置过程。

设置您的 DynamoDB 表

要开始此教程,需要首先使用以下 AWS CloudFormation 模板预配置 AWS 资源:

aws cloudformation create-stack \ --stack-name AWSAppSyncTutorialForAmazonDynamoDB \ --template-url https://s3.us-west-2.amazonaws.com/awsappsync/resources/dynamodb/AmazonDynamoDBCFTemplate.yaml \ --capabilities CAPABILITY_NAMED_IAM

您可以单击此按钮,在 US West 2(俄勒冈)区域以您的 AWS 账户启动此 AWS CloudFormation 堆栈:

这会创建以下内容:

  • 用于保留 Post 数据的 DynamoDB 表,名为 AppSyncTutorial-Post

  • 一个 IAM 角色以及关联的 IAM 托管策略,允许 AWS AppSync 与 Post 表交互。

要了解堆栈和所创建资源的更多详细信息,请运行以下 CLI 命令:

aws cloudformation describe-stacks \ --stack-name AWSAppSyncTutorialForAmazonDynamoDB

稍后要删除资源,您可以运行以下操作:

aws cloudformation delete-stack \ --stack-name AWSAppSyncTutorialForAmazonDynamoDB

创建您的 GraphQL API

要在 AWS AppSync 中创建 GraphQL API:

  • 打开 AWS AppSync 控制台,选择 Create API (创建 API)

  • 将 API 的名称设置为 AWSAppSyncTutorial

  • 选择自定义架构

  • 选择 Create

AWS AppSync 控制台会使用 API 密钥身份验证模式为您创建新的 GraphQL API。您可以根据本教程后面的说明,使用控制台设置 GraphQL API 的其余部分,并针对它运行查询。

定义基本的 Post API

您已设置了 AWS AppSync GraphQL API,现在可以设置基本架构,允许对文章数据进行基本的创建、检索和删除操作。

在 AWS AppSync 控制台中,选择 Schema (架构) 选项卡。在 Schema (架构) 窗格中,将内容替换为以下代码,然后选择 Save (保存)

schema { query: Query mutation: Mutation } type Query { getPost(id: ID): 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! version: Int! }

此架构定义 Post 类型,执行操作以添加并获取 Post 对象。

配置 DynamoDB 表的数据源

接下来,将架构中定义的查询和更改链接到 AppSyncTutorial-PostDynamoDB 表。

首先,AWS AppSync 需要知道您的表。要实现此目的,需要在 AWS AppSync 中设置数据源:

  • 选择 Data source (数据源) 选项卡。

  • 选择 New (新建) 创建新的数据源。

  • 对于数据源名称,输入 PostDynamoDBTable

  • 对于数据源类型,选择 Amazon DynamoDB table (Amazon DynamoDB 表)

  • 对于区域,选择 US-WEST-2

  • 从表的列表中选择 AppSyncTutorial-PostDynamoDB 表。

  • Create or use an existing role (创建或使用现有角色) 部分选择 Existing role (现有角色)

  • 选择 Create

设置 addPost 解析程序 (DynamoDB PutItem)

AWS AppSync 知道有 DynamoDB 表后,您可以定义解析程序,将表与单个查询和更改链接。您要创建的第一个解析程序是 addPost 解析程序,用于在 AppSyncTutorial-PostDynamoDB 表中创建一个文章。

解析程序具有以下组件:

  • GraphQL 架构中的位置,用于附加解析程序。在本例中,您将设置 addPost 类型的 Mutation 字段的解析程序。调用者调用 mutation { addPost(...){...} } 时,将调用此解析程序。

  • 此解析程序所用的数据源。在本例中,您要使用之前定义的 PostDynamoDBTable 数据源,这样您就可以在 AppSyncTutorial-Post DynamoDB 表中添加条目。

  • 请求映射模板。请求映射模板的目的是将调用者的传入请求转换为指令,这样 AWS AppSync 就可以针对 DynamoDB 执行操作。

  • 响应映射模板。响应映射模板的任务是将 DynamoDB 的响应转换回 GraphQL 期待获得的内容。如果 DynamoDB 中的数据形态与 GraphQL 中的 Post 类型不同,此模板很有用。但在此例中它们的形态相同,所以只用于传递数据。

设置解析程序:

  • 选择 Schema (架构) 选项卡。

  • 在右侧的 Data types (数据类型) 窗格中,找到 Mutation (更改) 类型上的 addPost 字段,然后选择 Attach (附加)

  • Data source name (数据源名称) 中,选择 PostDynamoDBTable

  • Configure the request mapping template (配置请求映射模板) 中,粘贴以下内容:

    { "version" : "2017-02-28", "operation" : "PutItem", "key" : { "id" : $util.dynamodb.toDynamoDBJson($context.arguments.id) }, "attributeValues" : { "author" : $util.dynamodb.toDynamoDBJson($context.arguments.author), "title" : $util.dynamodb.toDynamoDBJson($context.arguments.title), "content" : $util.dynamodb.toDynamoDBJson($context.arguments.content), "url" : $util.dynamodb.toDynamoDBJson($context.arguments.url), "ups" : { "N" : 1 }, "downs" : { "N" : 0 }, "version" : { "N" : 1 } } }

    注意:为所有键和属性值指定了类型。例如,您将 author 字段设置为 { "S" : "${context.arguments.author}" }S 部分告知 AWS AppSync 和 DynamoDB,此值为字符串。实际的值由 author 参数填充。与此类似,version 字段是一个数字字段,因为它使用 N 作为类型。最后,您还将初始化 upsdownsversion 字段。

    您已在此教程中指定了 GraphQL ID! 类型,它作为客户端参数的一部分,会为插入 DynamoDB 的新项目编制索引。AWS AppSync 还附带了一个称为 $utils.autoId() 的实用程序,用于自动生成 ID,也能以 "id" : { "S" : "${$utils.autoId()}" } 的形式使用。然后,就可以在 id: ID! 的架构定义中省去 addPost(),因为它将自动插入。在此教程中,您不会使用此方法,但在写入 DynamoDB 表时,可以考虑使用这种方便的方法。

    有关映射模板的更多信息,请参阅 解析程序映射模板概述参考文档。有关 GetItem 请求映射的更多信息,请参阅 GetItem 参考文档。有关类型的更多信息,请参阅类型系统(请求映射)参考文档。

  • Configure the response mapping template (配置响应映射模板) 中,粘贴以下内容:

    $utils.toJson($context.result)

    注意:由于 AppSyncTutorial-Post 表中的数据形态与 GraphQL 中 Post 类型的形态完全匹配,响应映射模板只会直接传递结果。还请注意,此教程中的所有示例均使用同一响应映射模板,所以您只需创建一个文件。

  • 选择 Save

调用 API 来添加文章

现在解析程序已设置完成,AWS AppSync 可以将传入的 addPost 更改转换为 DynamoDB PutItem 操作。现在,您可以运行一个更改,在表中添加内容。

  • 选择 Queries 选项卡。

  • Queries (查询) 窗格中,粘贴以下更改:

    mutation addPost { addPost( id: 123 author: "AUTHORNAME" title: "Our first post!" content: "This is our first post." url: "https://aws.amazon.com/appsync/" ) { id author title content url ups downs version } }
  • 选择 Execute query (执行查询)(橙色播放按钮)。

  • 新创建的文章的结果应出现在查询窗格右侧的结果窗格中。如下所示:

    { "data": { "addPost": { "id": "123", "author": "AUTHORNAME", "title": "Our first post!", "content": "This is our first post.", "url": "https://aws.amazon.com/appsync/", "ups": 1, "downs": 0, "version": 1 } } }

以下是具体过程:

  • AWS AppSync 收到 addPost 更改请求。

  • AWS AppSync 利用请求和请求映射模板,生成请求映射文档。如下所示:

    { "version" : "2017-02-28", "operation" : "PutItem", "key" : { "id" : { "S" : "123" } }, "attributeValues" : { "author": { "S" : "AUTHORNAME" }, "title": { "S" : "Our first post!" }, "content": { "S" : "This is our first post." }, "url": { "S" : "https://aws.amazon.com/appsync/" }, "ups" : { "N" : 1 }, "downs" : { "N" : 0 }, "version" : { "N" : 1 } } }
  • AWS AppSync 使用请求映射文档生成并执行 DynamoDBPutItem 请求。

  • AWS AppSync 将 PutItem 请求的结果转换回 GraphQL 类型。

    { "id" : "123", "author": "AUTHORNAME", "title": "Our first post!", "content": "This is our first post.", "url": "https://aws.amazon.com/appsync/", "ups" : 1, "downs" : 0, "version" : 1 }
  • 通过响应映射文档进行传递,没有变化。

  • 在 GraphQL 响应中返回新创建的对象。

设置 getPost 解析程序 (DynamoDB GetItem)

您已能够将数据添加到 AppSyncTutorial-PostDynamoDB 表中,现在您需要设置 getPost 查询,以便从 AppSyncTutorial-Post 表中检索数据。为了实现此目的,您要设置另一解析程序。

  • 选择 Schema (架构) 选项卡。

  • 在右侧的 Data types (数据类型) 窗格中,找到 Query (查询) 类型上的 getPost 字段,然后选择 Attach (附加)

  • Data source name (数据源名称) 中,选择 PostDynamoDBTable

  • Configure the request mapping template (配置请求映射模板) 中,粘贴以下内容:

    { "version" : "2017-02-28", "operation" : "GetItem", "key" : { "id" : $util.dynamodb.toDynamoDBJson($ctx.args.id) } }
  • Configure the response mapping template (配置响应映射模板) 中,粘贴以下内容:

    $utils.toJson($context.result)
  • 选择 Save

调用 API 以获取文章

现在解析程序已设置完成,AWS AppSync 也知道了如何将传入 getPost 查询转换为 DynamoDBGetItem 操作。现在,您可以运行查询,检索之前创建的文章。

  • 选择 Queries (查询) 选项卡。

  • Queries (查询) 窗格中粘贴以下内容:

    query getPost { getPost(id:123) { id author title content url ups downs version } }
  • 选择 Execute query (执行查询)(橙色播放按钮)。

  • 从 DynamoDB 中检索到的文章应出现在查询窗格右侧的结果窗格中。如下所示:

    { "data": { "getPost": { "id": "123", "author": "AUTHORNAME", "title": "Our first post!", "content": "This is our first post.", "url": "https://aws.amazon.com/appsync/", "ups": 1, "downs": 0, "version": 1 } } }

以下是具体过程:

  • AWS AppSync 收到 getPost 查询请求。

  • AWS AppSync 利用请求和请求映射模板,生成请求映射文档。如下所示:

    { "version" : "2017-02-28", "operation" : "GetItem", "key" : { "id" : { "S" : "123" } } }
  • AWS AppSync 使用请求映射文档生成并执行 DynamoDB GetItem 请求。

  • AWS AppSync 将 GetItem 请求的结果转换回 GraphQL 类型。

    { "id" : "123", "author": "AUTHORNAME", "title": "Our first post!", "content": "This is our first post.", "url": "https://aws.amazon.com/appsync/", "ups" : 1, "downs" : 0, "version" : 1 }
  • 通过响应映射文档进行传递,没有变化。

  • 在响应中返回检索到的对象。

创建 updatePost 更改 (DynamoDB UpdateItem)

您已经能在 DynamoDB 中创建并检索 Post 对象了。现在,您要设置一项新的更改,以便更新对象。您将使用 UpdateItem DynamoDB 操作实现此目的。

  • 选择 Schema (架构) 选项卡。

  • Schema (架构) 窗格中修改 Mutation 类型,添加新的 updatePost 更改,如下所示:

    type Mutation { updatePost( id: ID!, author: String!, title: String!, content: String!, url: String! ): Post addPost( author: String! title: String! content: String! url: String! ): Post! }
  • 选择 Save

  • 在右侧的 Data types (数据类型) 窗格中,在 Mutation (更改) 类型上找到新创建的 updatePost 字段,然后选择 Attach (附加)

  • Data source name (数据源名称) 中,选择 PostDynamoDBTable

  • Configure the request mapping template (配置请求映射模板) 中,粘贴以下内容:

    { "version" : "2017-02-28", "operation" : "UpdateItem", "key" : { "id" : $util.dynamodb.toDynamoDBJson($context.arguments.id) }, "update" : { "expression" : "SET author = :author, title = :title, content = :content, #url = :url ADD version :one", "expressionNames": { "#url" : "url" }, "expressionValues": { ":author" : $util.dynamodb.toDynamoDBJson($context.arguments.author), ":title" : $util.dynamodb.toDynamoDBJson($context.arguments.title), ":content" : $util.dynamodb.toDynamoDBJson($context.arguments.content), ":url" : $util.dynamodb.toDynamoDBJson($context.arguments.url), ":one" : { "N": 1 } } } }

    注意:此解析程序使用的是 DynamoDB UpdateItem,它与 PutItem 操作截然不同。您只要求 DynamoDB 更新某些属性,而不会写入整个项目。这是使用 DynamoDB 更新表达式实现的。表达式本身是在 expression 部分的 update 字段中指定的。它会设置 authortitlecontent 和 URL 属性,还会递增 version 字段。要使用的值不会出现在表达式本身;表达式中的占位符名称以冒号打头,并在 expressionValues 字段中进行定义。最后,DynamoDB 还有一些保留关键字是不能出现在 expression 中的。例如,url 是保留关键字,所以要更新 url 字段,您可使用名称占位符,并在 expressionNames 字段中定义它们。

    有关 UpdateItem 请求映射的更多信息,请参阅 UpdateItem 参考文档。有关如何编写更新表达式的更多信息,请参阅 DynamoDB UpdateExpressions 文档

  • Configure the response mapping template (配置响应映射模板) 中,粘贴以下内容:

    $utils.toJson($context.result)

调用 API 以更新文章

现在解析程序已设置完成,AWS AppSync 也知道了如何将传入 update 更改转换为 DynamoDBUpdate 操作。现在,您可以运行更改,以更新您之前写入的项目。

  • 选择 Queries (查询) 选项卡。

  • Queries (查询) 窗格中,粘贴以下更改。您还需要更新 id 参数,改为您之前记下的值。

    mutation updatePost { updatePost( id:"123" author: "A new author" title: "An updated author!" content: "Now with updated content!" url: "https://aws.amazon.com/appsync/" ) { id author title content url ups downs version } }
  • 选择 Execute query (执行查询)(橙色播放按钮)。

  • 在 DynamoDB 中更新的文章应出现在查询窗格右侧的结果窗格中。如下所示:

    { "data": { "updatePost": { "id": "123", "author": "A new author", "title": "An updated author!", "content": "Now with updated content!", "url": "https://aws.amazon.com/appsync/", "ups": 1, "downs": 0, "version": 2 } } }

在本示例中,upsdowns 字段未修改,因为请求映射模板没有要求 AWS AppSync 和 DynamoDB 对这些字段执行任何操作。此外,version 字段会增加 1,因为您要求 AWS AppSync 和 DynamoDB 向 version 字段增加 1。

修改 updatePost 解析程序 (DynamoDB UpdateItem)

updatePost 更改看上去不错,但它有两个主要问题:

  • 如果您只希望更新一个字段,则必须更新所有字段。

  • 如果两个人同时修改对象,您可能会丢失信息。

为了解决这些问题,您要修改 updatePost 更改,做到只修改请求中指定的参数,然后在 UpdateItem 操作中添加条件。

  • 选择 Schema (架构) 选项卡。

  • Schema (架构) 窗格中修改 Mutation 类型中的 updatePost 字段,删除 authortitlecontenturl 参数的感叹号,确保 id 字段不变。这样它们就会成为可选参数。还要新增一个必需 expectedVersion 参数。

    type Mutation { updatePost( id: ID!, author: String, title: String, content: String, url: String, expectedVersion: Int! ): Post addPost( author: String! title: String! content: String! url: String! ): Post! }
  • 选择 Save

  • 在右侧的 Data types (数据类型) 窗格中,找到 Mutation (更改) 类型新创建的 updatePost 字段。

  • 选择 PostDynamoDBTable 以打开现有解析程序。

  • Configure the request mapping template (配置请求映射模板) 中修改请求映射模板,如下所示:

    { "version" : "2017-02-28", "operation" : "UpdateItem", "key" : { "id" : $util.dynamodb.toDynamoDBJson($context.arguments.id) }, ## Set up some space to keep track of things you're updating ** #set( $expNames = {} ) #set( $expValues = {} ) #set( $expSet = {} ) #set( $expAdd = {} ) #set( $expRemove = [] ) ## Increment "version" by 1 ** $!{expAdd.put("version", ":one")} $!{expValues.put(":one", { "N" : 1 })} ## Iterate through each argument, skipping "id" and "expectedVersion" ** #foreach( $entry in $context.arguments.entrySet() ) #if( $entry.key != "id" && $entry.key != "expectedVersion" ) #if( (!$entry.value) && ("$!{entry.value}" == "") ) ## If the argument is set to "null", then remove that attribute from the item in DynamoDB ** #set( $discard = ${expRemove.add("#${entry.key}")} ) $!{expNames.put("#${entry.key}", "$entry.key")} #else ## Otherwise set (or update) the attribute on the item in DynamoDB ** $!{expSet.put("#${entry.key}", ":${entry.key}")} $!{expNames.put("#${entry.key}", "$entry.key")} $!{expValues.put(":${entry.key}", { "S" : "${entry.value}" })} #end #end #end ## Start building the update expression, starting with attributes you're going to SET ** #set( $expression = "" ) #if( !${expSet.isEmpty()} ) #set( $expression = "SET" ) #foreach( $entry in $expSet.entrySet() ) #set( $expression = "${expression} ${entry.key} = ${entry.value}" ) #if ( $foreach.hasNext ) #set( $expression = "${expression}," ) #end #end #end ## Continue building the update expression, adding attributes you're going to ADD ** #if( !${expAdd.isEmpty()} ) #set( $expression = "${expression} ADD" ) #foreach( $entry in $expAdd.entrySet() ) #set( $expression = "${expression} ${entry.key} ${entry.value}" ) #if ( $foreach.hasNext ) #set( $expression = "${expression}," ) #end #end #end ## Continue building the update expression, adding attributes you're going to REMOVE ** #if( !${expRemove.isEmpty()} ) #set( $expression = "${expression} REMOVE" ) #foreach( $entry in $expRemove ) #set( $expression = "${expression} ${entry}" ) #if ( $foreach.hasNext ) #set( $expression = "${expression}," ) #end #end #end ## Finally, write the update expression into the document, along with any expressionNames and expressionValues ** "update" : { "expression" : "${expression}" #if( !${expNames.isEmpty()} ) ,"expressionNames" : $utils.toJson($expNames) #end #if( !${expValues.isEmpty()} ) ,"expressionValues" : $utils.toJson($expValues) #end }, "condition" : { "expression" : "version = :expectedVersion", "expressionValues" : { ":expectedVersion" : $util.dynamodb.toDynamoDBJson($context.arguments.expectedVersion) } } }
  • 选择 Save

此模板是一个更复杂的示例。它演示了映射模板的强大功能和灵活性。它遍历所有参数,跳过 idexpectedVersion。如果参数设为某一值,它会要求 AWS AppSync 和 DynamoDB 在 DynamoDB 中更新对象的这一属性。如果属性设为 null,它会要求 AWS AppSync 和 DynamoDB 从文章对象中删除该属性。如果未指定参数,该属性会保留原样。它还会递增 version 字段。

还有一个新的 condition 部分。您可通过条件表达式告知 AWS AppSync 和 DynamoDB,根据执行操作前已在 DynamoDB 中的对象状态,判断请求是否应成功。在本例中,您只希望 DynamoDB 中当前存在的项目的 version 字段与 expectedVersion 参数完全匹配时,UpdateItem 请求才应成功。

有关条件表达式的更多信息,请参阅条件表达式参考文档。

调用 API 以更新文章

让我们尝试用新的解析程序更新 Post 对象:

  • 选择 Queries (查询) 选项卡。

  • Queries (查询) 窗格中,粘贴以下更改。您还需要更新 id 参数,改为您之前记下的值。

    mutation updatePost { updatePost( id:123 title: "An empty story" content: null expectedVersion: 2 ) { id author title content url ups downs version } }
  • 选择 Execute query (执行查询)(橙色播放按钮)。

  • 在 DynamoDB 中更新的文章应出现在查询窗格右侧的结果窗格中。如下所示:

    { "data": { "updatePost": { "id": "123", "author": "A new author", "title": "An empty story", "content": null, "url": "https://aws.amazon.com/appsync/", "ups": 1, "downs": 0, "version": 3 } } }

在此请求中,您要求 AWS AppSync 和 DynamoDB 仅更新 titlecontent 字段。它不会处理所有其他字段(除了递增 version 字段)。您将 title 属性设为新值,并删除文章的 content 属性。authorurlupsdowns 字段没有变化。

请尝试再次执行更改请求,保持请求完全不变。您可以看到类似以下内容的响应:

{ "data": { "updatePost": null }, "errors": [ { "path": [ "updatePost" ], "data": { "id": "123", "author": "A new author", "title": "An empty story", "content": null, "url": "https://aws.amazon.com/appsync/", "ups": 1, "downs": 0, "version": 3 }, "errorType": "DynamoDB:ConditionalCheckFailedException", "locations": [ { "line": 2, "column": 3 } ], "message": "The conditional request failed (Service: AmazonDynamoDBv2; Status Code: 400; Error Code: ConditionalCheckFailedException; Request ID: ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ)" } ] }

请求失败,因为条件表达式评估为 false:

  • 您第一次运行请求时,DynamoDB 中文章的 version 字段值为 2,与 expectedVersion 参数匹配。请求成功,意味着 DynamoDB 中 version 字段递增到 3

  • 您第二次运行请求时,DynamoDB 中文章的 version 字段值为 3,与 expectedVersion 参数不匹配。

这种模式通常被称为乐观锁

AWS AppSync DynamoDB 解析程序的一项功能是,它会返回 DynamoDB 中文章对象的当前值。您可以在 GraphQL 响应的 data 部分的 errors 字段中找到这个值。您的应用程序可以利用此信息决定应如何继续。在本例中,您可以看到在 DynamoDB 中对象的 version 字段设为 3,所以您只需将 expectedVersion 参数更新为 3,请求就可以再次成功了。

有关如何处理条件检查失败的更多信息,请参阅条件表达式映射模板参考文档。

创建 upvotePost 和 downvotePost 更改 (DynamoDB UpdateItem)

Post 类型有 upsdowns 字段,用于记录赞同投票和反对投票,但现在还无法通过 API 使用它们。让我们添加一些更改,对文章进行赞同和反对投票。

  • 选择 Schema (架构) 选项卡。

  • Schema (架构) 窗格中修改 Mutation 类型,添加新的 upvotePostdownvotePost 更改,如下所示:

    type Mutation { upvotePost(id: ID!): Post downvotePost(id: ID!): Post updatePost( id: ID!, author: String, title: String, content: String, url: String, expectedVersion: Int! ): Post addPost( author: String!, title: String!, content: String!, url: String! ): Post! }
  • 选择 Save

  • 在右侧的 Data types (数据类型) 窗格中,在 Mutation (更改) 类型上找到新创建的 upvotePost 字段,然后选择 Attach (附加)

  • Data source name (数据源名称) 中,选择 PostDynamoDBTable

  • Configure the request mapping template (配置请求映射模板) 中,粘贴以下内容:

    { "version" : "2017-02-28", "operation" : "UpdateItem", "key" : { "id" : $util.dynamodb.toDynamoDBJson($context.arguments.id) }, "update" : { "expression" : "ADD ups :plusOne, version :plusOne", "expressionValues" : { ":plusOne" : { "N" : 1 } } } }
  • Configure the response mapping template (配置响应映射模板) 中,粘贴以下内容:

    $utils.toJson($context.result)
  • 选择 Save

  • 在右侧的 Data types (数据类型) 窗格中,在 Mutation (更改) 类型上找到新创建的 downvotePost 字段,然后选择 Attach (附加)

  • Data source name (数据源名称) 中,选择 PostDynamoDBTable

  • Configure the request mapping template (配置请求映射模板) 中,粘贴以下内容:

    { "version" : "2017-02-28", "operation" : "UpdateItem", "key" : { "id" : $util.dynamodb.toDynamoDBJson($context.arguments.id) }, "update" : { "expression" : "ADD downs :plusOne, version :plusOne", "expressionValues" : { ":plusOne" : { "N" : 1 } } } }
  • Configure the response mapping template (配置响应映射模板) 中,粘贴以下内容:

    $utils.toJson($context.result)
  • 选择 Save

调用 API,为文章投赞同票或反对票

现在新的解析程序已设置完成,AWS AppSync 也知道了如何将传入的 upvotePostdownvote 更改转换为 DynamoDB UpdateItem 操作。现在您可以运行更改,为之前创建的文章投赞同票或反对票。

  • 选择 Queries (查询) 选项卡。

  • Queries (查询) 窗格中,粘贴以下更改。您还需要更新 id 参数,改为您之前记下的值。

    mutation votePost { upvotePost(id:123) { id author title content url ups downs version } }
  • 选择 Execute query (执行查询)(橙色播放按钮)。

  • 在 DynamoDB 中更新的文章应出现在查询窗格右侧的结果窗格中。如下所示:

    { "data": { "upvotePost": { "id": "123", "author": "A new author", "title": "An empty story", "content": null, "url": "https://aws.amazon.com/appsync/", "ups": 6, "downs": 0, "version": 4 } } }
  • 再选择几次 (执行查询) 按钮。您应看到,每次您执行查询时,upsversion 字段均会递增 1。

  • 更改查询以调用 downvotePost 更改,如下所示:

    mutation votePost { downvotePost(id:123) { id author title content url ups downs version } }
  • 选择 Execute query (执行查询)(橙色播放按钮)。这次您应看到,每次您执行查询时,downsversion 字段均会递增 1。

    { "data": { "downvotePost": { "id": "123", "author": "A new author", "title": "An empty story", "content": null, "url": "https://aws.amazon.com/appsync/", "ups": 6, "downs": 4, "version": 12 } } }

设置 deletePost 解析程序 (DynamoDB DeletePost)

接下来您要设置的更改是删除一个文章。您要使用 DeleteItemDynamoDB 操作。

  • 选择 Schema (架构) 选项卡。

  • Schema (架构) 窗格中修改 Mutation 类型,添加新的 deletePost 更改,如下所示:

    type Mutation { deletePost(id: ID!, expectedVersion: Int): Post upvotePost(id: ID!): Post downvotePost(id: ID!): Post updatePost( id: ID!, author: String, title: String, content: String, url: String, expectedVersion: Int! ): Post addPost( author: String!, title: String!, content: String!, url: String! ): Post! }

    这次您将 expectedVersion 字段设为可选,稍后在添加请求映射模板时将对此进行说明。

  • 选择 Save

  • 在右侧的 Data types (数据类型) 窗格中,在 Mutation (更改) 类型上找到新创建的 delete 字段,然后选择 Attach (附加)

  • Data source name (数据源名称) 中,选择 PostDynamoDBTable

  • Configure the request mapping template (配置请求映射模板) 中,粘贴以下内容:

    { "version" : "2017-02-28", "operation" : "DeleteItem", "key": { "id": $util.dynamodb.toDynamoDBJson($context.arguments.id) } #if( $context.arguments.containsKey("expectedVersion") ) ,"condition" : { "expression" : "attribute_not_exists(id) OR version = :expectedVersion", "expressionValues" : { ":expectedVersion" : $util.dynamodb.toDynamoDBJson($context.arguments.expectedVersion) } } #end }

    注意expectedVersion 参数是可选的。如果调用者在请求中设置 expectedVersion 参数,模板会添加一个条件,只有在项目已删除或者 DynamoDB 中文章的 version 属性与 expectedVersion 完全匹配的情况下,才允许 DeleteItem 请求成功。如果未设置此参数,则 DeleteItem 请求中不指定条件表达式。无论 version 的值是什么,也无论该项目是否在 DynamoDB 中存在,该请求都成功。

  • Configure the response mapping template (配置响应映射模板) 中,粘贴以下内容:

    $utils.toJson($context.result)

    注意:即使您要删除一个项目,如果该项目不是已经删除,还是可以返回要删除的项目。

  • 选择 Save

有关 DeleteItem 请求映射的更多信息,请参阅 DeleteItem 参考文档。

调用 API 以删除文章

现在解析程序已设置完成,AWS AppSync 也知道了如何将传入 delete 更改转换为 DynamoDBDeleteItem 操作。现在,您可以运行更改,从表中删除一些内容。

  • 选择 Queries (查询) 选项卡。

  • Queries (查询) 窗格中,粘贴以下更改。您还需要更新 id 参数,改为您之前记下的值。

    mutation deletePost { deletePost(id:123) { id author title content url ups downs version } }
  • 选择 Execute query (执行查询)(橙色播放按钮)。

  • 文章已经从 DynamoDB 中删除。请注意,AWS AppSync 返回已从 DynamoDB 中删除的项目的值,该项目应出现在查询窗格右侧的结果窗格中。如下所示:

    { "data": { "deletePost": { "id": "123", "author": "A new author", "title": "An empty story", "content": null, "url": "https://aws.amazon.com/appsync/", "ups": 6, "downs": 4, "version": 12 } } }

只有调用的 deletePost 将项目从 DynamoDB 中实际删除,才会返回值。

  • 再次选择 Execute query (执行查询)

  • 调用仍是成功的,但没有返回值。

    { "data": { "deletePost": null } }

现在让我们尝试删除一篇文章,但这次要指定 expectedValue。但首先您需要创建一个新文章,因为您刚刚删除了一直在使用的文章。

  • Queries (查询) 窗格中,粘贴以下更改:

    mutation addPost { addPost( id:123 author: "AUTHORNAME" title: "Our second post!" content: "A new post." url: "https://aws.amazon.com/appsync/" ) { id author title content url ups downs version } }
  • 选择 Execute query (执行查询)(橙色播放按钮)。

  • 新创建的文章的结果应出现在查询窗格右侧的结果窗格中。记下新建对象的 id,因为一会您将用到它。如下所示:

    { "data": { "addPost": { "id": "123", "author": "AUTHORNAME", "title": "Our second post!", "content": "A new post.", "url": "https://aws.amazon.com/appsync/", "ups": 1, "downs": 0, "version": 1 } } }

现在让我们尝试删除这个文章,但放入 expectedVersion 的错误值:

  • Queries (查询) 窗格中,粘贴以下更改。您还需要更新 id 参数,改为您之前记下的值。

    mutation deletePost { deletePost( id:123 expectedVersion: 9999 ) { id author title content url ups downs version } }
  • 选择 Execute query (执行查询)(橙色播放按钮)。

    { "data": { "deletePost": null }, "errors": [ { "path": [ "deletePost" ], "data": { "id": "123", "author": "AUTHORNAME", "title": "Our second post!", "content": "A new post.", "url": "https://aws.amazon.com/appsync/", "ups": 1, "downs": 0, "version": 1 }, "errorType": "DynamoDB:ConditionalCheckFailedException", "locations": [ { "line": 2, "column": 3 } ], "message": "The conditional request failed (Service: AmazonDynamoDBv2; Status Code: 400; Error Code: ConditionalCheckFailedException; Request ID: ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ)" } ] }

    请求失败,因为条件表达式评估为 false:DynamoDB 中文章的 version 值与参数中指定的 expectedValue 不匹配。对象的当前值返回到 GraphQL 响应的 data 部分的 errors 字段中。

  • 重试请求,但更正 expectedVersion

    mutation deletePost { deletePost( id:123 expectedVersion: 1 ) { id author title content url ups downs version } }
  • 选择 Execute query (执行查询)(橙色播放按钮)。

  • 这次请求成功,而且返回了从 DynamoDB 中删除的值:

    { "data": { "deletePost": { "id": "123", "author": "AUTHORNAME", "title": "Our second post!", "content": "A new post.", "url": "https://aws.amazon.com/appsync/", "ups": 1, "downs": 0, "version": 1 } } }
  • 再次选择 Execute query (执行查询)

  • 调用仍是成功的,但这次没有返回值,因为 DynamoDB 中的文章已被删除。

{ "data": { "deletePost": null } }

设置 allPost 解析程序 (DynamoDB Scan)

到目前为止,只有当您知道您想查看的每个帖子的 id 时,API 才有用。让我们添加新的解析程序,它可以返回表中的所有文章。

  • 选择 Schema (架构) 选项卡。

  • Schema (架构) 窗格中修改 Query 类型,添加新的 allPost 查询,如下所示:

    type Query { allPost(count: Int, nextToken: String): PaginatedPosts! getPost(id: ID): Post }
  • 添加新 PaginationPosts 类型:

    type PaginatedPosts { posts: [Post!]! nextToken: String }
  • 选择 Save (保存)

  • 在右侧的 Data types (数据类型) 窗格中,在 Query (查询) 类型上找到新创建的 allPost 字段,然后选择 Attach (附加)

  • Data source name (数据源名称) 中,选择 PostDynamoDBTable

  • Configure the request mapping template (配置请求映射模板) 中,粘贴以下内容:

    { "version" : "2017-02-28", "operation" : "Scan" #if( ${context.arguments.count} ) ,"limit": $util.toJson($context.arguments.count) #end #if( ${context.arguments.nextToken} ) ,"nextToken": $util.toJson($context.arguments.nextToken) #end }

    此解析程序有两个可选参数:count 指定单次调用可返回的项目数量上限;nextToken 可用于检索下一组结果(稍后您将展示 nextToken 的值来自何处)。

  • Configure the response mapping template (配置响应映射模板) 中,粘贴以下内容:

    { "posts": $utils.toJson($context.result.items) #if( ${context.result.nextToken} ) ,"nextToken": $util.toJson($context.result.nextToken) #end }

    注意:此响应映射模板与目前我们所用到的其他模板均不同。allPost 查询的结果是 PaginatedPosts,其中包含一组文章和一个分页标记。此对象的形态与 AWS AppSync DynamoDB 解析程序返回的对象不同:文章列表在 AWS AppSync DynamoDB 解析程序的结果中称为 items,但在 PaginatedPosts 中称为 posts

  • 选择 Save (保存)

有关 Scan 请求映射的更多信息,请参阅 Scan 参考文档。

调用 API 以扫描所有文章

现在解析程序已设置完成,AWS AppSync 也知道了如何将传入 allPost 查询转换为 DynamoDBScan 操作。现在您可以扫描整个表,检索所有文章。

在进行尝试之前,您需要在表中填充一些数据,因为您已经删除了之前使用的所有内容。

  • 选择 Queries (查询) 选项卡。

  • Queries (查询) 窗格中,粘贴以下更改:

    mutation addPost { post1: addPost(id:1 author: "AUTHORNAME" title: "A series of posts, Volume 1" content: "Some content" url: "https://aws.amazon.com/appsync/" ) { title } post2: addPost(id:2 author: "AUTHORNAME" title: "A series of posts, Volume 2" content: "Some content" url: "https://aws.amazon.com/appsync/" ) { title } post3: addPost(id:3 author: "AUTHORNAME" title: "A series of posts, Volume 3" content: "Some content" url: "https://aws.amazon.com/appsync/" ) { title } post4: addPost(id:4 author: "AUTHORNAME" title: "A series of posts, Volume 4" content: "Some content" url: "https://aws.amazon.com/appsync/" ) { title } post5: addPost(id:5 author: "AUTHORNAME" title: "A series of posts, Volume 5" content: "Some content" url: "https://aws.amazon.com/appsync/" ) { title } post6: addPost(id:6 author: "AUTHORNAME" title: "A series of posts, Volume 6" content: "Some content" url: "https://aws.amazon.com/appsync/" ) { title } post7: addPost(id:7 author: "AUTHORNAME" title: "A series of posts, Volume 7" content: "Some content" url: "https://aws.amazon.com/appsync/" ) { title } post8: addPost(id:8 author: "AUTHORNAME" title: "A series of posts, Volume 8" content: "Some content" url: "https://aws.amazon.com/appsync/" ) { title } post9: addPost(id:9 author: "AUTHORNAME" title: "A series of posts, Volume 9" content: "Some content" url: "https://aws.amazon.com/appsync/" ) { title } }
  • 选择 Execute query (执行查询)(橙色播放按钮)。

现在,让我们扫描表,每次返回 5 个结果。

  • Queries (查询) 窗格中粘贴以下查询:

    query allPost { allPost(count: 5) { posts { id title } nextToken } }
  • 选择 Execute query (执行查询)(橙色播放按钮)。

  • 最前面的 5 个文章应出现在查询窗格右侧的结果窗格中。如下所示:

    { "data": { "allPost": { "posts": [ { "id": "5", "title": "A series of posts, Volume 5" }, { "id": "1", "title": "A series of posts, Volume 1" }, { "id": "6", "title": "A series of posts, Volume 6" }, { "id": "9", "title": "A series of posts, Volume 9" }, { "id": "7", "title": "A series of posts, Volume 7" } ], "nextToken": "eyJ2ZXJzaW9uIjoxLCJ0b2tlbiI6IkFRSUNBSGo4eHR0RG0xWXhUa1F0cEhXMEp1R3B0M1B3eThOSmRvcG9ad2RHYjI3Z0lnRkJEdXdUK09hcnovRGhNTGxLTGdMUEFBQUI1akNDQWVJR0NTcUdTSWIzRFFFSEJxQ0NBZE13Z2dIUEFnRUFNSUlCeUFZSktvWklodmNOQVFjQk1CNEdDV0NHU0FGbEF3UUJMakFSQkF6ajFodkhKU1paT1pncTRaUUNBUkNBZ2dHWnJiR1dQWGxkMDB1N0xEdGY4Z2JsbktzRjRua1VCcks3TFJLcjZBTFRMeGFwVGJZMDRqOTdKVFQyYVRwSzdzbVdtNlhWWFVCTnFIOThZTzBWZHVkdDI2RlkxMHRqMDJ2QTlyNWJTUWpTbWh6NE5UclhUMG9KZWJSQ2JJbXBlaDRSVlg0Tis0WTVCN1IwNmJQWWQzOVhsbTlUTjBkZkFYMVErVCthaXZoNE5jMk50RitxVmU3SlJ5WmpzMEFkSGduM3FWd2VrOW5oeFVVd3JlK1loUks5QkRzemdiMDlmZmFPVXpzaFZ4cVJRbC93RURlOTcrRmVJdXZNby9NZ1F6dUdNbFRyalpNR3FuYzZBRnhwa0VlZTFtR0FwVDFISElUZlluakptYklmMGUzUmcxbVlnVHVSbDh4S0trNmR0QVoraEhLVDhuNUI3VnF4bHRtSnlNUXBrZGl6KzkyL3VzNDl4OWhrMnVxSW01ZFFwMjRLNnF0dm9ZK1BpdERuQTc5djhzb0grVytYT3VuQ2NVVDY4TVZ1Wk5KYkRuSEFSSEVlaTlVNVBTelU5RGZ6d2pPdmhqWDNJMWhwdWUrWi83MDVHVjlPQUxSTGlwZWZPeTFOZFhwZTdHRDZnQW00bUJUK2c1eC9Ec3ZDbWVnSDFDVXRTdHVuU1ZFa2JpZytQRC9oMUwyRTNqSHhVQldaa28yU256WUc0cG0vV1RSWkFVZHZuQT09In0=" } } }

您可以看到 5 个结果,还有一个 nextToken,可用于获得下一组结果。

  • 更新 allPost 查询,加入上一组结果的 nextToken

    query allPost { allPost( count: 5 nextToken: "eyJ2ZXJzaW9uIjoxLCJ0b2tlbiI6IkFRSUNBSGo4eHR0RG0xWXhUa1F0cEhXMEp1R3B0M1B3eThOSmRvcG9ad2RHYjI3Z0lnRlluNktJRWl6V0ZlR3hJOVJkaStrZUFBQUI1akNDQWVJR0NTcUdTSWIzRFFFSEJxQ0NBZE13Z2dIUEFnRUFNSUlCeUFZSktvWklodmNOQVFjQk1CNEdDV0NHU0FGbEF3UUJMakFSQkF5cW8yUGFSZThnalFpemRCTUNBUkNBZ2dHWk1JODhUNzhIOFVUZGtpdFM2ZFluSWRyVDg4c2lkN1RjZzB2d1k3VGJTTWpSQ2U3WjY3TkUvU2I1dWNETUdDMmdmMHErSGJSL0pteGRzYzVEYnE1K3BmWEtBdU5jSENJdWNIUkJ0UHBPWVdWdCtsS2U5L1pNcWdocXhrem1RaXI1YnIvQkt6dU5hZmJCdE93NmtoM2Jna1BKM0RjWWhpMFBGbmhMVGg4TUVGSjBCcXg3RTlHR1V5N0tUS0JLZlV3RjFQZ0JRREdrNzFYQnFMK2R1S2IrVGtZZzVYMjFrc3NyQmFVTmNXZmhTeXE0ZUJHSWhqZWQ5c3VKWjBSSTc2ZnVQdlZkR3FLNENjQmxHYXhpekZnK2pKK1FneEU1SXduRTNYYU5TR0I4QUpmamR2bU1wbUk1SEdvWjlMUUswclczbG14RDRtMlBsaTNLaEVlcm9pem5zcmdINFpvcXIrN2ltRDN3QkJNd3BLbGQzNjV5Nnc4ZnMrK2FnbTFVOUlKOFFrOGd2bEgySHFROHZrZXBrMWlLdWRIQ25LaS9USnBlMk9JeEVPazVnRFlzRTRUU09HUlVJTkxYY2MvdW1WVEpBMUthV2hWTlAvdjNlSnlZQUszbWV6N2h5WHVXZ1BkTVBNWERQdTdjVnVRa3EwK3NhbGZOd2wvSUx4bHNyNDVwTEhuVFpyRWZvVlV1bXZ5S2VKY1RUU1lET05hM1NwWEd2UT09In0=" ) { posts { id author } nextToken } }
  • 选择 Execute query (执行查询)(橙色播放按钮)。

  • 其余的 4 个文章应出现在查询窗格右侧的结果窗格中。在这一组结果中没有 nextToken,因为您已经找到了所有的 9 个文章,没有其他内容了。如下所示:

    { "data": { "allPost": { "posts": [ { "id": "2", "title": "A series of posts, Volume 2" }, { "id": "3", "title": "A series of posts, Volume 3" }, { "id": "4", "title": "A series of posts, Volume 4" }, { "id": "8", "title": "A series of posts, Volume 8" } ], "nextToken": null } } }

设置 allPostsByAuthor 解析程序 (DynamoDB Query)

除了可以扫描 DynamoDB 中的所有文章,您还可以查询 DynamoDB,检索由特定作者创建的文章。您之前创建的 DynamoDB 表已有一个 GlobalSecondaryIndex,名为 author-index。您可以将它用于 DynamoDBQuery 操作,检索由特定作者创建的所有文章。

  • 选择 Schema (架构) 选项卡。

  • Schema (架构) 窗格中修改 Query 类型,添加新的 allPostsByAuthor 查询,如下所示:

    type Query { allPostsByAuthor(author: String!, count: Int, nextToken: String): PaginatedPosts! allPost(count: Int, nextToken: String): PaginatedPosts! getPost(id: ID): Post }

    注意:这次使用与 allPost 查询相同的 PaginatedPosts 类型。

  • 选择 Save (保存)

  • 在右侧的 Data types (数据类型) 窗格中,在 Query (查询) 类型上找到新创建的 allPostsByAuthor 字段,然后选择 Attach (附加)

  • Data source name (数据源名称) 中,选择 PostDynamoDBTable

  • Configure the request mapping template (配置请求映射模板) 中,粘贴以下内容:

    { "version" : "2017-02-28", "operation" : "Query", "index" : "author-index", "query" : { "expression": "author = :author", "expressionValues" : { ":author" : $util.dynamodb.toDynamoDBJson($context.arguments.author) } } #if( ${context.arguments.count} ) ,"limit": $util.toJson($context.arguments.count) #end #if( ${context.arguments.nextToken} ) ,"nextToken": "${context.arguments.nextToken}" #end }

    allPost 解析程序相似,此解析程序也有两个可选参数:count指定单次调用可返回的项目数量上限;nextToken 可用于检索下一组结果(nextToken 的值可从上次调用获得)。

  • Configure the response mapping template (配置响应映射模板) 中,粘贴以下内容:

    { "posts": $utils.toJson($context.result.items) #if( ${context.result.nextToken} ) ,"nextToken": $util.toJson($context.result.nextToken) #end }

    注意:此处使用的响应映射模板与 allPost 解析程序中所用的相同。

  • 选择 Save (保存)

了解有关 Query 请求映射的更多信息,请参阅 Query 参考文档。

调用 API 以查询某一作者的所有文章

现在解析程序已设置完成,AWS AppSync 也知道了如何针对 author-index 索引将传入 allPostsByAuthor 更改转换为 DynamoDBQuery 操作。现在,您可以查询表,检索某一作者的所有文章。

但首先我们需要在表中再填充一些文章,因为目前所有文章都是同一作者。

  • 选择 Queries (查询) 选项卡。

  • Queries (查询) 窗格中,粘贴以下更改:

    mutation addPost { post1: addPost(id:10 author: "Nadia" title: "The cutest dog in the world" content: "So cute. So very, very cute." url: "https://aws.amazon.com/appsync/" ) { author, title } post2: addPost(id:11 author: "Nadia" title: "Did you know...?" content: "AppSync works offline?" url: "https://aws.amazon.com/appsync/" ) { author, title } post3: addPost(id:12 author: "Steve" title: "I like GraphQL" content: "It's great" url: "https://aws.amazon.com/appsync/" ) { author, title } }
  • 选择 Execute query (执行查询)(橙色播放按钮)。

现在,让我们查询表,返回作者为 Nadia 的所有文章。

  • Queries (查询) 窗格中粘贴以下查询:

    query allPostsByAuthor { allPostsByAuthor(author: "Nadia") { posts { id title } nextToken } }
  • 选择 Execute query (执行查询)(橙色播放按钮)。

  • 作者为 Nadia 的全部文章应出现在查询窗格右侧的结果窗格中。如下所示:

    { "data": { "allPostsByAuthor": { "posts": [ { "id": "10", "title": "The cutest dog in the world" }, { "id": "11", "title": "Did you know...?" } ], "nextToken": null } } }

Query 的分页方式与 Scan 相同。例如,如果我们查找作者为 AUTHORNAME 的所有文章,每次显示 5 个结果。

  • Queries (查询) 窗格中粘贴以下查询:

    query allPostsByAuthor { allPostsByAuthor( author: "AUTHORNAME" count: 5 ) { posts { id title } nextToken } }
  • 选择 Execute query (执行查询)(橙色播放按钮)。

  • 作者为 AUTHORNAME 的全部文章应出现在查询窗格右侧的结果窗格中。如下所示:

    { "data": { "allPostsByAuthor": { "posts": [ { "id": "6", "title": "A series of posts, Volume 6" }, { "id": "4", "title": "A series of posts, Volume 4" }, { "id": "2", "title": "A series of posts, Volume 2" }, { "id": "7", "title": "A series of posts, Volume 7" }, { "id": "1", "title": "A series of posts, Volume 1" } ], "nextToken": "eyJ2ZXJzaW9uIjoxLCJ0b2tlbiI6IkFRSUNBSGo4eHR0RG0xWXhUa1F0cEhXMEp1R3B0M1B3eThOSmRvcG9ad2RHYjI3Z0lnSExqRnVhVUR3ZUhEZ2QzNGJ2QlFuY0FBQUNqekNDQW9zR0NTcUdTSWIzRFFFSEJxQ0NBbnd3Z2dKNEFnRUFNSUlDY1FZSktvWklodmNOQVFjQk1CNEdDV0NHU0FGbEF3UUJMakFSQkF5Qkg4Yk1obW9LVEFTZHM3SUNBUkNBZ2dKQ3dISzZKNlJuN3pyYUVKY1pWNWxhSkNtZW1KZ0F5N1dhZkc2UEdTNHpNQzJycTkwZHFJTFV6Z25wck9Gd3pMS3VOQ2JvUXc3VDI5eCtnVExIbGg4S3BqbzB1YjZHQ3FwcDhvNDVmMG9JbDlmdS9JdjNXcFNNSXFKTXZ1MEVGVWs1VzJQaW5jZGlUaVRtZFdYWlU1bkV2NkgyRFBRQWZYYlNnSmlHSHFLbmJZTUZZM0FTdmRIL0hQaVZBb1RCMk1YZkg0eGJOVTdEbjZtRFNhb2QwbzdHZHJEWDNtODQ1UXBQUVNyUFhHemY0WDkyajhIdlBCSWE4Smcrb0RxbHozUVQ5N2FXUXdYWWU2S0h4emI1ejRITXdEdXEyRDRkYzhoMi9CbW10MzRMelVGUVIyaExSZGRaZ0xkdzF5cHJZdFZwY3dEc1d4UURBTzdOcjV2ZEp4VVR2TVhmODBRSnp1REhXREpTVlJLdDJwWmlpaXhXeGRwRmNod1BzQ3d2aVBqMGwrcWFFWU1jMXNQbENkVkFGem43VXJrSThWbS8wWHlwR2xZb3BSL2FkV0xVekgrbGMrYno1ZEM2SnVLVXdtY1EyRXlZeDZiS0Izbi9YdUViWGdFeU5PMWZTdE1rRlhyWmpvMVpzdlYyUFRjMzMrdEs0ZDhkNkZrdjh5VVR6WHhJRkxIaVNsOUx6VVdtT3BCaWhrTFBCT09jcXkyOHh1UmkzOEM3UFRqMmN6c3RkOUo1VUY0azBJdUdEbVZzM2xjdWg1SEJjYThIeXM2aEpvOG1HbFpMNWN6R2s5bi8vRE1EbDY3RlJraG5QNFNhSDBpZGI5VFEvMERLeFRBTUdhcWpPaEl5ekVqd2ZDQVJleFdlbldyOGlPVkhScDhGM25WZVdvbFRGK002N0xpdi9XNGJXdDk0VEg3b0laUU5lYmZYKzVOKy9Td25Hb1dyMTlWK0pEb2lIRVFLZ1cwMWVuYjZKUXo5Slh2Tm95ZzF3RnJPVmxGc2xwNlRHa1BlN2Rnd2IrWT0ifQ==" } } }
  • 用上次查询返回的值更新 nextToken 参数,如下所示:

    query allPostsByAuthor { allPostsByAuthor( author: "AUTHORNAME" count: 5 nextToken: "eyJ2ZXJzaW9uIjoxLCJ0b2tlbiI6IkFRSUNBSGo4eHR0RG0xWXhUa1F0cEhXMEp1R3B0M1B3eThOSmRvcG9ad2RHYjI3Z0lnSExqRnVhVUR3ZUhEZ2QzNGJ2QlFuY0FBQUNqekNDQW9zR0NTcUdTSWIzRFFFSEJxQ0NBbnd3Z2dKNEFnRUFNSUlDY1FZSktvWklodmNOQVFjQk1CNEdDV0NHU0FGbEF3UUJMakFSQkF5Qkg4Yk1obW9LVEFTZHM3SUNBUkNBZ2dKQ3dISzZKNlJuN3pyYUVKY1pWNWxhSkNtZW1KZ0F5N1dhZkc2UEdTNHpNQzJycTkwZHFJTFV6Z25wck9Gd3pMS3VOQ2JvUXc3VDI5eCtnVExIbGg4S3BqbzB1YjZHQ3FwcDhvNDVmMG9JbDlmdS9JdjNXcFNNSXFKTXZ1MEVGVWs1VzJQaW5jZGlUaVRtZFdYWlU1bkV2NkgyRFBRQWZYYlNnSmlHSHFLbmJZTUZZM0FTdmRIL0hQaVZBb1RCMk1YZkg0eGJOVTdEbjZtRFNhb2QwbzdHZHJEWDNtODQ1UXBQUVNyUFhHemY0WDkyajhIdlBCSWE4Smcrb0RxbHozUVQ5N2FXUXdYWWU2S0h4emI1ejRITXdEdXEyRDRkYzhoMi9CbW10MzRMelVGUVIyaExSZGRaZ0xkdzF5cHJZdFZwY3dEc1d4UURBTzdOcjV2ZEp4VVR2TVhmODBRSnp1REhXREpTVlJLdDJwWmlpaXhXeGRwRmNod1BzQ3d2aVBqMGwrcWFFWU1jMXNQbENkVkFGem43VXJrSThWbS8wWHlwR2xZb3BSL2FkV0xVekgrbGMrYno1ZEM2SnVLVXdtY1EyRXlZeDZiS0Izbi9YdUViWGdFeU5PMWZTdE1rRlhyWmpvMVpzdlYyUFRjMzMrdEs0ZDhkNkZrdjh5VVR6WHhJRkxIaVNsOUx6VVdtT3BCaWhrTFBCT09jcXkyOHh1UmkzOEM3UFRqMmN6c3RkOUo1VUY0azBJdUdEbVZzM2xjdWg1SEJjYThIeXM2aEpvOG1HbFpMNWN6R2s5bi8vRE1EbDY3RlJraG5QNFNhSDBpZGI5VFEvMERLeFRBTUdhcWpPaEl5ekVqd2ZDQVJleFdlbldyOGlPVkhScDhGM25WZVdvbFRGK002N0xpdi9XNGJXdDk0VEg3b0laUU5lYmZYKzVOKy9Td25Hb1dyMTlWK0pEb2lIRVFLZ1cwMWVuYjZKUXo5Slh2Tm95ZzF3RnJPVmxGc2xwNlRHa1BlN2Rnd2IrWT0ifQ==" ) { posts { id title } nextToken } }
  • 选择 Execute query (执行查询)(橙色播放按钮)。

  • 作者为 AUTHORNAME 的剩余文章应出现在查询窗格右侧的结果窗格中。如下所示:

    { "data": { "allPostsByAuthor": { "posts": [ { "id": "8", "title": "A series of posts, Volume 8" }, { "id": "5", "title": "A series of posts, Volume 5" }, { "id": "3", "title": "A series of posts, Volume 3" }, { "id": "9", "title": "A series of posts, Volume 9" } ], "nextToken": null } } }

使用集

到目前为止,Post 类型一直是平面的键/值对象。您也可以利用 AWS AppSyncDynamoDB 解析程序建模复杂的对象,例如集、列表和映射。

让我们更新 Post 类型,加入标签。一个文章可以没有标签,也可以有多个标签;标签作为字符串集存储在 DynamoDB 中。您还将设置一些更改,用于添加并删除标签;还要用一个新查询扫描具有特定标签的文章。

  • 选择 Schema (架构) 选项卡。

  • Schema (架构) 窗格中修改 Post 类型,添加新的 tags 字段,如下所示:

    type Post { id: ID! author: String title: String content: String url: String ups: Int! downs: Int! version: Int! tags: [String!] }
  • Schema (架构) 窗格中修改 Query 类型,添加新的 allPostsByTag 查询,如下所示:

    type Query { allPostsByTag(tag: String!, count: Int, nextToken: String): PaginatedPosts! allPostsByAuthor(author: String!, count: Int, nextToken: String): PaginatedPosts! allPost(count: Int, nextToken: String): PaginatedPosts! getPost(id: ID): Post }
  • Schema (架构) 窗格中修改 Mutation 类型,添加新的 addTagremoveTag 更改,如下所示:

    type Mutation { addTag(id: ID!, tag: String!): Post removeTag(id: ID!, tag: String!): Post deletePost(id: ID!, expectedVersion: Int): Post upvotePost(id: ID!): Post downvotePost(id: ID!): Post updatePost( id: ID!, author: String, title: String, content: String, url: String, expectedVersion: Int! ): Post addPost( author: String!, title: String!, content: String!, url: String! ): Post! }
  • 选择 Save (保存)

  • 在右侧的 Data types (数据类型) 窗格中,在 Query (查询) 类型上找到新创建的 allPostsByTag 字段,然后选择 Attach (附加)

  • Data source name (数据源名称) 中,选择 PostDynamoDBTable

  • Configure the request mapping template (配置请求映射模板) 中,粘贴以下内容:

    { "version" : "2017-02-28", "operation" : "Scan", "filter": { "expression": "contains (tags, :tag)", "expressionValues": { ":tag": $util.dynamodb.toDynamoDBJson($context.arguments.tag) } } #if( ${context.arguments.count} ) ,"limit": $util.toJson($context.arguments.count) #end #if( ${context.arguments.nextToken} ) ,"nextToken": $util.toJson($context.arguments.nextToken) #end }
  • Configure the response mapping template (配置响应映射模板) 中,粘贴以下内容:

    { "posts": $utils.toJson($context.result.items) #if( ${context.result.nextToken} ) ,"nextToken": $util.toJson($context.result.nextToken) #end }
  • 选择 Save (保存)

  • 在右侧的 Data types (数据类型) 窗格中,在 Mutation (更改) 类型上找到新创建的 addTag 字段,然后选择 Attach (附加)

  • Data source name (数据源名称) 中,选择 PostDynamoDBTable

  • Configure the request mapping template (配置请求映射模板) 中,粘贴以下内容:

    { "version" : "2017-02-28", "operation" : "UpdateItem", "key" : { "id" : $util.dynamodb.toDynamoDBJson($context.arguments.id) }, "update" : { "expression" : "ADD tags :tags, version :plusOne", "expressionValues" : { ":tags" : { "SS": [ $util.toJson($context.arguments.tag) ] }, ":plusOne" : { "N" : 1 } } } }
  • Configure the response mapping template (配置响应映射模板) 中,粘贴以下内容:

    $utils.toJson($context.result)
  • 选择 Save (保存)

  • 在右侧的 Data types (数据类型) 窗格中,在 Mutation (更改) 类型上找到新创建的 removeTag 字段,然后选择 Attach (附加)

  • Data source name (数据源名称) 中,选择 PostDynamoDBTable

  • Configure the request mapping template (配置请求映射模板) 中,粘贴以下内容:

    { "version" : "2017-02-28", "operation" : "UpdateItem", "key" : { "id" : $util.dynamodb.toDynamoDBJson($context.arguments.id) }, "update" : { "expression" : "DELETE tags :tags ADD version :plusOne", "expressionValues" : { ":tags" : { "SS": [ $util.toJson($context.arguments.tag) ] }, ":plusOne" : { "N" : 1 } } } }
  • Configure the response mapping template (配置响应映射模板) 中,粘贴以下内容:

    $utils.toJson($context.result)
  • 选择 Save (保存)

调用 API 以处理标签

现在您已设置解析程序,AWS AppSync 也知道了如何将传入的 addTagremoveTagallPostsByTag 请求转换为 DynamoDBUpdateItemScan 操作。

我们选择您之前创建的一个文章进行尝试。例如,我们使用作者为 Nadia 的一篇文章。

  • 选择 Queries (查询) 选项卡。

  • Queries (查询) 窗格中粘贴以下查询:

    query allPostsByAuthor { allPostsByAuthor( author: "Nadia" ) { posts { id title } nextToken } }
  • 选择 Execute query (执行查询)(橙色播放按钮)。

  • Nadia 的全部文章应出现在查询窗格右侧的结果窗格中。如下所示:

    { "data": { "allPostsByAuthor": { "posts": [ { "id": "10", "title": "The cutest dog in the world" }, { "id": "11", "title": "Did you known...?" } ], "nextToken": null } } }
  • 让我们使用标题为 "The cutest dog in the world" 的文章。记下其 id,因为您稍后将用到它。

现在,让我们尝试添加 dog 标签。

  • Queries (查询) 窗格中,粘贴以下更改。您还需要更新 id 参数,改为您之前记下的值。

    mutation addTag { addTag(id:10 tag: "dog") { id title tags } }
  • 选择 Execute query (执行查询)(橙色播放按钮)。

  • 文章已更新,添加了这个新标签。

    { "data": { "addTag": { "id": "10", "title": "The cutest dog in the world", "tags": [ "dog" ] } } }

您可以添加更多标签,如下所示:

  • 更新更改,将 tag 参数改为 puppy

    mutation addTag { addTag(id:10 tag: "puppy") { id title tags } }
  • 选择 Execute query (执行查询)(橙色播放按钮)。

  • 文章已更新,添加了这个新标签。

    { "data": { "addTag": { "id": "10", "title": "The cutest dog in the world", "tags": [ "dog", "puppy" ] } } }

您也可以删除标签:

  • Queries (查询) 窗格中,粘贴以下更改。您还需要更新 id 参数,改为您之前记下的值。

    mutation removeTag { removeTag(id:10 tag: "puppy") { id title tags } }
  • 选择 Execute query (执行查询)(橙色播放按钮)。

  • 文章已更新,puppy 标签已删除。

    { "data": { "addTag": { "id": "10", "title": "The cutest dog in the world", "tags": [ "dog" ] } } }

您也可以搜索具有某一标签的所有文章:

  • Queries (查询) 窗格中粘贴以下查询:

    query allPostsByTag { allPostsByTag(tag: "dog") { posts { id title tags } nextToken } }
  • 选择 Execute query (执行查询)(橙色播放按钮)。

  • 将返回具有 dog 标签的所有文章,如下所示:

    { "data": { "allPostsByTag": { "posts": [ { "id": "10", "title": "The cutest dog in the world", "tags": [ "dog", "puppy" ] } ], "nextToken": null } } }

使用列表和映射

您不仅可以使用 DynamoDB 集,还可以使用 DynamoDB 列表和映射,在单独的对象中建模复杂的数据。

我们可以为文章添加评论功能。在 DynamoDB 中,该功能作为 Post 对象的映射列表对象进行建模。

注意:在真正的应用程序中,您会在评论自身的表中对评论进行建模。在本教程中,您仅将评论添加到 Post 表。

  • 选择 Schema (架构) 选项卡。

  • Schema (架构) 窗格中,添加新的 Comment 类型,如下所示:

    type Comment { author: String! comment: String! }
  • Schema (架构) 窗格中修改 Post 类型,添加新的 comments 字段,如下所示:

    type Post { id: ID! author: String title: String content: String url: String ups: Int! downs: Int! version: Int! tags: [String!] comments: [Comment!] }
  • Schema (架构) 窗格中修改 Mutation 类型,添加新的 addComment 更改,如下所示:

    type Mutation { addComment(id: ID!, author: String!, comment: String!): Post addTag(id: ID!, tag: String!): Post removeTag(id: ID!, tag: String!): Post deletePost(id: ID!, expectedVersion: Int): Post upvotePost(id: ID!): Post downvotePost(id: ID!): Post updatePost( id: ID!, author: String, title: String, content: String, url: String, expectedVersion: Int! ): Post addPost( author: String!, title: String!, content: String!, url: String! ): Post! }
  • 选择 Save (保存)

  • 在右侧的 Data types (数据类型) 窗格中,在 Mutation (更改) 类型上找到新创建的 addComment 字段,然后选择 Attach (附加)

  • Data source name (数据源名称) 中,选择 PostDynamoDBTable

  • Configure the request mapping template (配置请求映射模板) 中,粘贴以下内容:

    { "version" : "2017-02-28", "operation" : "UpdateItem", "key" : { "id" : $util.toJson($context.arguments.id) }, "update" : { "expression" : "SET comments = list_append(if_not_exists(comments, :emptyList), :newComment) ADD version :plusOne", "expressionValues" : { ":emptyList": { "L" : [] }, ":newComment" : { "L" : [ { "M": { "author": $util.toJson($context.arguments.author), "comment": $util.toJson($context.arguments.comment) }} ] }, ":plusOne" : { "N" : 1 } } } }

    此更新表达式将一个列表(包含新评论)追加到现有的 comments 列表中。如果这个列表不存在,将创建它。

  • Configure the response mapping template (配置响应映射模板) 中,粘贴以下内容:

    $utils.toJson($context.result)
  • 选择 Save (保存)

调用 API 以添加评论

现在您已设置解析程序,AWS AppSync 也知道了如何将传入的 addComment 请求转换为 DynamoDBUpdateItem 操作。

让我们尝试在您已添加标签的文章中添加评论。

  • 选择 Queries (查询) 选项卡。

  • Queries (查询) 窗格中粘贴以下查询:

    mutation addComment { addComment( id:10 author: "Steve" comment: "Such a cute dog." ) { id comments { author comment } } }
  • 选择 Execute query (执行查询)(橙色播放按钮)。

  • Nadia 的全部文章应出现在查询窗格右侧的结果窗格中。如下所示:

    { "data": { "addComment": { "id": "10", "comments": [ { "author": "Steve", "comment": "Such a cute dog." } ] } } }

如果您多次执行该请求,列表中将追加多条评论。

结论

在本教程中,您构建了 API,以使用 AWS AppSync 和 GraphQL 处理 DynamoDB 中的 Post 对象。有关更多信息,请参阅解析程序映射模板参考

要进行清理,您可以从控制台中删除 AppSync GraphQL API。

要删除您针对本教程创建的 DynamoDB 表和 IAM 角色,您可以运行以下代码删除 AWSAppSyncTutorialForAmazonDynamoDB 堆栈,或者访问 AWS CloudFormation 控制台删除堆栈:

aws cloudformation delete-stack \ --stack-name AWSAppSyncTutorialForAmazonDynamoDB