教程:使用 DynamoDB 解析器创建简单的后期应用程序 - AWS AppSync

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

教程:使用 DynamoDB 解析器创建简单的后期应用程序

注意

我们现在主要支持 APPSYNC _JS 运行时及其文档。请考虑在此处使用 APPSYNC _JS 运行时及其指南。

本教程展示了如何将自己的 Amazon DynamoDB 表带到 GraphQL 并将其连接到 AWS AppSync GraphQL。API

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

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

设置您的 DynamoDB 表

要开始本教程,首先需要按照以下步骤配置 AWS 资源。

  1. 使用以下 AWS CloudFormation 模板置备 AWS 资源CLI:

    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

    或者,您可以在自己的 AWS 账户中在美国西部 2(俄勒冈)地区启动以下 AWS CloudFormation 堆栈。

    Blue button labeled "Launch Stack" with an arrow icon indicating an action to start.

    这会创建以下内容:

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

    • 允许 AWS AppSync 与Post表交互的IAM角色和关联的IAM托管策略。

  2. 要查看有关堆栈和已创建资源的更多详细信息,请运行以下CLI命令:

    aws cloudformation describe-stacks --stack-name AWSAppSyncTutorialForAmazonDynamoDB
  3. 稍后要删除资源,您可以运行以下操作:

    aws cloudformation delete-stack --stack-name AWSAppSyncTutorialForAmazonDynamoDB

创建你的 GraphQL API

要在下面创建 GraphQLAPI,请执行以下操作: AWS AppSync

  1. 登录 AWS Management Console 并打开AppSync 控制台

    1. APIs仪表板中,选择创建API

  2. 在 “自定义您的” API 或 “从亚马逊 DynamoDB 导入” 窗口下,选择从头开始构建。

    1. 选择同一窗口右侧的开始

  3. API名称字段中,将的名称设置API为AWSAppSyncTutorial

  4. 选择创建

AWS AppSync 控制台使用API密钥身份验证模式API为您创建一个新的 GraphQL。您可以使用控制台设置 GraphQL 的其余部分,API并在本教程的其余部分中对其运行查询。

定义基本帖子 API

现在,您已经创建了 AWS AppSync GraphQLAPI,可以设置一个基本架构,允许基本创建、检索和删除帖子数据。

  1. 登录 AWS Management Console 并打开AppSync 控制台

    1. APIs仪表板中,选择API您刚刚创建的。

  2. 侧边栏中,选择架构

    1. 架构窗格中,将内容替换为以下代码:

      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! }
  3. 选择保存

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

为 DynamoDB 表配置数据来源

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

首先, AWS AppSync 需要注意你的表格。您可以通过在以下位置设置数据源来实现此目的 AWS AppSync:

  1. 登录 AWS Management Console 并打开AppSync 控制台

    1. APIs控制面板中,选择您的 GraphQL API。

    2. 侧边栏中,选择数据来源

  2. 选择创建数据来源

    1. 对于数据来源名称,输入 PostDynamoDBTable

    2. 对于数据来源类型,选择 Amazon DynamoDB 表

    3. 对于区域,请选择美国-WEST 2

    4. 对于表名,选择 AppSyncTutorial-Pos t DynamoDB 表。

    5. 创建新IAM角色(推荐)或选择具有该lambda:invokeFunctionIAM权限的现有角色。现有角色需要具有一个信任策略,如附加数据来源一节中所述。

      以下是具有对资源执行操作所需权限的IAM策略示例:

      { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "lambda:invokeFunction" ], "Resource": [ "arn:aws:lambda:us-west-2:123456789012:function:myFunction", "arn:aws:lambda:us-west-2:123456789012:function:myFunction:*" ] } ] }
  3. 选择创建

设置 addPost 解析器 (DynamoD PutItem B)

在知道 DynamoDB 表之后 AWS AppSync ,您可以通过定义解析器将其链接到各个查询和变更。您创建的第一个解析器是 addPost 解析器,可用于在 AppSyncTutorial-Post DynamoDB 表中创建文章。

解析器具有以下组件:

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

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

  • 请求映射模板。请求映射模板的目的是接收来自调用者的传入请求,并将其转换为要对 DynamoDB 执行的 AWS AppSync 指令。

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

设置解析器:

  1. 登录 AWS Management Console 并打开AppSync 控制台

    1. APIs控制面板中,选择您的 GraphQL API。

    2. 侧边栏中,选择数据来源

  2. 选择创建数据来源

    1. 对于数据来源名称,输入 PostDynamoDBTable

    2. 对于数据来源类型,选择 Amazon DynamoDB 表

    3. 对于区域,请选择美国-WEST 2

    4. 对于表名,选择 AppSyncTutorial-Pos t DynamoDB 表。

    5. 创建新IAM角色(推荐)或选择具有该lambda:invokeFunctionIAM权限的现有角色。现有角色需要具有一个信任策略,如附加数据来源一节中所述。

      以下是具有对资源执行操作所需权限的IAM策略示例:

      { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "lambda:invokeFunction" ], "Resource": [ "arn:aws:lambda:us-west-2:123456789012:function:myFunction", "arn:aws:lambda:us-west-2:123456789012:function:myFunction:*" ] } ] }
  3. 选择创建

  4. 选择架构选项卡。

  5. 在右侧的 “数据类型” 窗格中,找到 “突变” 类型上的addPost字段,然后选择 “附加”。

  6. “操作” 菜单中,选择 “更新运行时”,然后选择 “设备解析器”(VTL仅限)

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

  8. 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部分向 DynamoDB 表示该值将是一个字符串值。 AWS AppSync 实际的值由 author 参数填充。与此类似,version 字段是一个数字字段,因为它使用 N 作为类型。最后,您还将初始化 upsdownsversion 字段。

    在本教程中,您已指定 GraphQL ID! 类型(用于索引插入到 DynamoDB 的新项目)作为客户端参数的一部分。 AWS AppSync 附带了一个名为的自动生成身份的实用程序$utils.autoId(),您也可以以以下形式使用该实用程序"id" : { "S" : "${$utils.autoId()}" }。然后,就可以在 id: ID! 的架构定义中省去 addPost(),因为它将自动插入。您不会在本教程中使用该技术,但在写入到 DynamoDB 表时,您应该将其视为一种很好的做法。

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

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

    $utils.toJson($context.result)

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

  10. 选择保存

致电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 使用请求映射文档生成并执行 DynamoDB PutItem 请求。

  • 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 解析器 (DynamoD GetItem B)

您现在能够将数据添加到 AppSyncTutorial-Post DynamoDB 表中,您需要设置 getPost 查询,以使其可以从 AppSyncTutorial-Post 表中检索该数据。为了实现此目的,您要设置另一解析器。

  • 选择架构选项卡。

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

  • “操作” 菜单中,选择 “更新运行时”,然后选择 “设备解析器”(VTL仅限)

  • 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)
  • 选择保存

致电API获取帖子

现在,解析器已经设置完毕, AWS AppSync 知道如何将传入的getPost查询转换为 DynamoDB GetItem 操作。现在,您可以运行查询,检索之前创建的文章。

  • 选择 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 }
  • 通过响应映射文档进行传递,没有变化。

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

或者,采用以下示例:

query getPost { getPost(id:123) { id author title } }

如果您的 getPost 查询仅需要 idauthortitle,您可以将请求映射模板更改为使用投影表达式仅指定您希望从 DynamoDB 表中获取的属性,以避免将不必要的数据从 DynamoDB 传输到 AWS AppSync。例如,请求映射模板可能类似于以下代码片段:

{ "version" : "2017-02-28", "operation" : "GetItem", "key" : { "id" : $util.dynamodb.toDynamoDBJson($ctx.args.id) }, "projection" : { "expression" : "#author, id, title", "expressionNames" : { "#author" : "author"} } }

创建 updatePost 突变 (DynamoD UpdateItem B)

到目前为止,您可以在 DynamoDB 中创建和检索 Post 对象。现在,您要设置一项新的变更,以便更新对象。您将使用 UpdateItem DynamoDB 操作来执行此操作。

  • 选择架构选项卡。

  • 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! }
  • 选择保存

  • 在右侧的 “数据类型” 窗格中,在 “突变” 类型上找到新创建的updatePost字段,然后选择 “附加”。

  • “操作” 菜单中,选择 “更新运行时”,然后选择 “设备解析器”(VTL仅限)

  • 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 } } } }

    注意:此解析器使用的是 D UpdateItem ynamoDB,这与操作有很大不同。 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突变转换为 DynamoDB 操作。Update现在,您可以运行变更,以更新您之前写入的项目。

  • 选择 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 向该字段添加 1。version

修改 updatePost 解析器 (DynamoDB UpdateItem)

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

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

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

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

  1. 选择架构选项卡。

  2. 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! }
  3. 选择保存

  4. 在右侧的 “数据类型” 窗格中,找到 “突变类型” 上的updatePost字段。

  5. 选择PostDynamoDBTable打开现有的解析器。

  6. 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) } } }
  7. 选择保存

此模板是一个更复杂的示例。它演示了映射模板的强大功能和灵活性。它遍历所有参数,跳过 idexpectedVersion。如果参数设置为某个值,它会要求 AWS AppSync 和 DynamoDB 在 DynamoDB 中更新该对象上的该属性。如果该属性设置为空,它会要求 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 (架构) 窗格中修改 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! }
  • 选择保存

  • 在右侧的 “数据类型” 窗格中,在 “突变” 类型上找到新创建的upvotePost字段,然后选择 “附加”。

  • “操作” 菜单中,选择 “更新运行时”,然后选择 “设备解析器”(VTL仅限)

  • 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)
  • 选择保存

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

  • 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)
  • 选择保存

致电 the 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解析器 (DynamoD DeleteItem B)

接下来您要设置的变更是删除一个文章。您将使用 DeleteItem DynamoDB 操作完成该操作。

  • 选择架构选项卡。

  • 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 字段设为可选,稍后在添加请求映射模板时将对此进行说明。

  • 选择保存

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

  • “操作” 菜单中,选择 “更新运行时”,然后选择 “设备解析器”(VTL仅限)

  • 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)

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

  • 选择保存

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

致电API删除帖子

现在解析器已经设置好了, AWS AppSync 知道如何将传入的delete突变转换为 DynamoDB 操作。DeleteItem现在,您可以运行变更,从表中删除一些内容。

  • 选择 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 扫描)

到目前为止,API只有当你知道要查看id的每篇文章时,才有用。让我们添加新的解析器,它可以返回表中的所有文章。

  • 选择架构选项卡。

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

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

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

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

  • “操作” 菜单中,选择 “更新运行时”,然后选择 “设备解析器”(VTL仅限)

  • 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,其中包含一组文章和一个分页标记。此对象的形状与 D AWS AppSync ynamoDB 解析器返回的形状不同:帖子列表在 DynamoDB 解析器结果items中被调用,但会被调用。 AWS AppSync posts PaginatedPosts

  • 选择保存

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

致电扫描所有帖子 API

现在,解析器已经设置完毕, AWS AppSync 知道如何将传入的allPost查询转换为 DynamoDB Scan 操作。现在您可以扫描整个表,检索所有文章。

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

  • 选择 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 } } }

设置 allPostsBy作者解析器(DynamoDB 查询)

除了扫描 DynamoDB 以查找所有文章以外,您还可以查询 DynamoDB 以检索特定作者创建的文章。您以前创建的 DynamoDB 表已具有一个名为 author-indexGlobalSecondaryIndex,您可以将其与 DynamoDB Query 操作一起使用以检索特定作者创建的所有文章。

  • 选择架构选项卡。

  • 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 类型。

  • 选择保存

  • 在右侧的 “数据类型” 窗格中,在 “查询” 类型上找到新创建的 “allPostsBy作者” 字段,然后选择 “附加”。

  • “操作” 菜单中,选择 “更新运行时”,然后选择 “设备解析器”(VTL仅限)

  • 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 解析器中所用的相同。

  • 选择保存

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

API致电查询作者的所有帖子

现在解析器已经设置好了, AWS AppSync 知道如何将传入的allPostsByAuthor突变转换为针对索引的 DynamoDB 操作Queryauthor-index现在,您可以查询表,检索某一作者的所有文章。

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

  • 选择 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 AppSyncDynamo数据库解析器对复杂的对象进行建模,例如集合、列表和地图。

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

  • 选择架构选项卡。

  • 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! }
  • 选择保存

  • 在右侧的数据类型窗格中,在查询类型上找到新创建的allPostsBy标签字段,然后选择附加

  • 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 }
  • 选择保存

  • 在右侧的 “数据类型” 窗格中,在 “突变” 类型上找到新创建的addTag字段,然后选择 “附加”。

  • 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)
  • 选择保存

  • 在右侧的 “数据类型” 窗格中,在 “突变” 类型上找到新创建的removeTag字段,然后选择 “附加”。

  • 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)
  • 选择保存

致电API使用 Tags

现在您已经设置了解析器, AWS AppSync 知道如何将传入的addTagremoveTag、和allPostsByTag请求转换为 D UpdateItem ynamo Scan DB 和操作。

我们选择您之前创建的一个文章进行尝试。例如,我们使用作者为 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 (架构) 窗格中,添加新的 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! }
  • 选择保存

  • 在右侧的 “数据类型” 窗格中,在 “突变” 类型上找到新创建的addComment字段,然后选择 “附加”。

  • 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 comments = list_append(if_not_exists(comments, :emptyList), :newComment) ADD version :plusOne", "expressionValues" : { ":emptyList": { "L" : [] }, ":newComment" : { "L" : [ { "M": { "author": $util.dynamodb.toDynamoDBJson($context.arguments.author), "comment": $util.dynamodb.toDynamoDBJson($context.arguments.comment) } } ] }, ":plusOne" : $util.dynamodb.toDynamoDBJson(1) } } }

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

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

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

致电API添加评论

现在您已经设置了解析器, AWS AppSync 知道如何将传入的addComment请求转换为 DynamoDB 操作UpdateItem

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

  • 选择 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." } ] } } }

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

结论

在本教程中,您构建了一个,它允许我们使用 AWS AppSync 和 API GraphQL 在 DynamoDB 中操作 Post 对象。有关更多信息,请参阅解析器映射模板参考

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

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

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