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

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

教程:DynamoDB 解析器

注意

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

本教程说明了如何将您自己的 Amazon DynamoDB 表添加到 Amazon AppSync 中,并将其连接到 GraphQL API。

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

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

设置您的 DynamoDB 表

要开始本教程,您需要先按照以下步骤预置 Amazon 资源。

  1. 在 CLI 中使用以下 Amazon CloudFormation 模板预置 Amazon 资源:

    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

    或者,可以在您的 Amazon 账户的 US-West-2(俄勒冈州)区域中启动以下 Amazon CloudFormation 堆栈。

    这会创建以下内容:

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

    • 一个 IAM 角色和关联的 IAM 托管策略,旨在允许 Amazon AppSync 与 Post 表交互。

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

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

    aws cloudformation delete-stack --stack-name AWSAppSyncTutorialForAmazonDynamoDB

创建您的 GraphQL API

要在 Amazon AppSync 中创建 GraphQL API,请执行以下操作:

  1. 登录到 Amazon Web Services Management Console,然后打开 AppSync 控制台

    1. API 控制面板中,选择创建 API

  2. 自定义您的 API 或从 Amazon DynamoDB 导入下面,选择从头开始构建

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

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

  4. 选择创建

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

定义基本文章 API

您现已创建 Amazon AppSync GraphQL API,您可以设置一个基本架构,以允许对文章数据执行基本创建、检索和删除操作。

  1. 登录到 Amazon Web Services Management Console,然后打开 AppSync 控制台

    1. API 控制面板中,选择您刚刚创建的 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 表。

首先,Amazon AppSync 需要识别您的表。您可以在 Amazon AppSync 中设置数据源以完成该操作:

  1. 登录到 Amazon Web Services Management Console,然后打开 AppSync 控制台

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

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

  2. 选择创建数据源

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

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

    3. 对于区域,选择 US-WEST-2

    4. 对于表名称,选择 AppSyncTutorial-Post DynamoDB 表。

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

      以下是一个示例 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 解析器 (DynamoDB PutItem)

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

解析器具有以下组件:

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

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

  • 请求映射模板。请求映射模板的用途是,获取来自调用方的传入请求,并将其转换为 Amazon AppSync 指令以对 DynamoDB 执行。

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

设置解析器:

  1. 登录到 Amazon Web Services Management Console,然后打开 AppSync 控制台

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

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

  2. 选择创建数据源

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

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

    3. 对于区域,选择 US-WEST-2

    4. 对于表名称,选择 AppSyncTutorial-Post DynamoDB 表。

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

      以下是一个示例 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. 在右侧的数据类型窗格中,找到 Mutation 类型上的 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 部分向 Amazon AppSync 和 DynamoDB 指示该值是字符串值。实际的值由 author 参数填充。与此类似,version 字段是一个数字字段,因为它使用 N 作为类型。最后,您还将初始化 upsdownsversion 字段。

    对于本教程,您已指定 GraphQL ID! 类型作为客户端参数的一部分,该类型对插入到 DynamoDB 的新项目编制索引。Amazon AppSync 附带一个名为 $utils.autoId() 的自动 ID 生成实用程序,您也可以按 "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 以添加文章

解析器现已设置完毕,Amazon 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 } } }

以下是具体过程:

  • Amazon AppSync 收到 addPost 变更请求。

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

  • Amazon 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-Post DynamoDB 表中,您需要设置 getPost 查询,以使其可以从 AppSyncTutorial-Post 表中检索该数据。为了实现此目的,您要设置另一解析器。

  • 选择架构选项卡。

  • 在右侧的数据类型窗格中,找到 Query 类型上的 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 以获取文章

解析器现已设置完毕,Amazon 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 } } }

以下是具体过程:

  • Amazon AppSync 收到 getPost 查询请求。

  • Amazon AppSync 获取该请求和请求映射模板,并生成请求映射文档。如下所示:

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

  • Amazon 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 传输到 Amazon AppSync。例如,请求映射模板可能类似于以下代码片段:

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

创建 updatePost 变更 (DynamoDB UpdateItem)

到目前为止,您可以在 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! }
  • 选择保存

  • 在右侧的数据类型窗格中,找到 Mutation 类型上的新创建的 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 } } } }

    注意:该解析器使用 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 以更新文章

解析器现已设置完毕,Amazon 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 字段,因为请求映射模板不要求 Amazon AppSync 和 DynamoDB 对这些字段执行任何操作。此外,version 字段增加 1,因为您要求 Amazon AppSync 和 DynamoDB 在 version 字段中添加 1。

修改 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. 在右侧的数据类型窗格中,找到 Mutation 类型的 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。如果参数设置为某个值,它要求 Amazon AppSync 和 DynamoDB 为 DynamoDB 中的对象更新该属性。如果该属性设置为 Null,它要求 Amazon AppSync 和 DynamoDB 从 Post 对象中删除该属性。如果未指定参数,该属性会保留原样。它还会递增 version 字段。

还有一个新的 condition 部分。通过使用条件表达式,您可以在执行操作之前根据 DynamoDB 中的已有对象的状态向 Amazon AppSync 和 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 } } }

在该请求中,您要求 Amazon 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 参数不匹配。

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

Amazon AppSync DynamoDB 解析器的一项功能是,它返回 DynamoDB 中的 Post 对象的当前值。您可以在 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! }
  • 选择保存

  • 在右侧的数据类型窗格中,找到 Mutation 类型上的新创建的 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)
  • 选择保存

调用 API,为文章点赞或差评

新的解析器现已设置完毕,Amazon 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 DeleteItem)

接下来您要设置的变更是删除一个文章。您将使用 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 以删除文章

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

  • 选择 Queries 选项卡。

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

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

  • 该文章已从 DynamoDB 中删除。请注意,Amazon 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 (架构) 窗格中修改 Query 类型,添加新的 allPost 查询,如下所示:

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

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

  • 在右侧的数据类型窗格中,找到 Query 类型上的新创建的 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,其中包含一组文章和一个分页标记。该对象的形状与从 Amazon AppSync DynamoDB 解析器返回的对象不同:文章列表在 items AppSync DynamoDB 解析器结果中命名为 Amazon,但在 PaginatedPosts 中命名为 posts

  • 选择保存

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

调用 API 以扫描所有文章

解析器现已设置完毕,Amazon 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 } } }

设置 allPostsByAuthor 解析器 (DynamoDB Query)

除了扫描 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 类型。

  • 选择保存

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

  • 操作菜单中,选择更新运行时,然后选择单位解析器 (仅限 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 以查询某一作者的所有文章

解析器现已设置完毕,Amazon AppSync 知道如何将传入的 allPostsByAuthor 变更转换为针对 author-index 索引的 DynamoDB Query 操作。现在,您可以查询表,检索某一作者的所有文章。

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

  • 选择 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 类型一直是平面键/值对象。您也可以使用 Amazon AppSync DynamoDB 解析器对复杂对象进行建模,例如集、列表和映射。

让我们更新 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! }
  • 选择保存

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

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

  • 在右侧的数据类型窗格中,找到 Mutation 类型上的新创建的 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)
  • 选择保存

  • 在右侧的数据类型窗格中,找到 Mutation 类型上的新创建的 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 以处理标签

您现已设置解析器,Amazon AppSync 知道如何将传入的 addTagremoveTagallPostsByTag 请求转换为 DynamoDB UpdateItemScan 操作。

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

  • 在右侧的数据类型窗格中,找到 Mutation 类型上的新创建的 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 以添加评论

您现已设置解析器,Amazon 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." } ] } } }

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

结论

在本教程中,您构建了一个 API,可用于通过 Amazon AppSync 和 GraphQL 处理 DynamoDB 中的 Post 对象。有关更多信息,请参阅解析器映射模板参考

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

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

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