AWS CDK 新增了 AppSync 的高级构建

首先

因为 AWSCDK 1.19.0 中的 AppSync 高级结构已经推出,让我感到非常开心,所以我写下了这篇文章!

AWSCDK是什么

CloudFormation(以下CFn)是一个可以使用TypeScript或Python等编程语言来编写模板的框架。 请查看官方网站以获取详细信息。

AWS AppSync是什么?

如果在AWS上使用GraphQL,可以选择AppSync这个服务。您可以指定DynamoDB或Lambda作为GraphQL的DataSource,并通过GraphQL的API进行操作。请查看官方网站以了解更多详细信息。

我們開始寫吧!

以前,使用AWSCDK编写AppSync时,必须使用类似于Cfnxxxx的低级构造才能完成。
因此,需要手动指定所有必要的属性,这非常麻烦…
在最新更新中,引入了高级构造,现在我将介绍其改变了什么写法。

GraphQL的架构文件,是一个GraphQL文件。

type Post {
  id: String,
  title: String,
  create_time: String
}
input InputPost {
  id: String
  title: String,
  create_time: String
}
type Query {
  all: [Post]
  query(id: String!, start: String!, end: String!): [Post]
}
type Mutation {
  save(input: InputPost!): Post
  delete(id: ID!): Post
}
type Schema {
  query: Query
  mutation: Mutation
}

源代码以堆栈形式展示

import cdk = require("@aws-cdk/core")
import { join } from "path"

import { UserPool, SignInType } from "@aws-cdk/aws-cognito"
import {
  GraphQLApi,
  FieldLogLevel,
  UserPoolDefaultAction,
  MappingTemplate
} from "@aws-cdk/aws-appsync"
import {
  TableProps,
  AttributeType,
  BillingMode,
  Table
} from "@aws-cdk/aws-dynamodb"

export class AppSyncStack extends cdk.Stack {
  constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
    super(scope, id, props)

    const userPool = new UserPool(this, "UserPool", {
      userPoolName: "DemoAPIUserPool",
      signInType: SignInType.USERNAME
    })

    const table = new Table(this, "CDKPostTable", {
      tableName: "CDKPostTable",
      partitionKey: {
        name: "id",
        type: AttributeType.STRING
      },
      sortKey: {
        name: "create_time",
        type: AttributeType.STRING
      },
      billingMode: BillingMode.PROVISIONED,
      readCapacity: 1,
      writeCapacity: 1
    })

    const api = new GraphQLApi(this, "PostAPI", {
      name: "PostAPI",
      logConfig: {
        fieldLogLevel: FieldLogLevel.ALL
      },
      userPoolConfig: {
        userPool,
        defaultAction: UserPoolDefaultAction.ALLOW
      },
      schemaDefinitionFile: join(__dirname, "schema.graphql")
    })

    const datasource = api.addDynamoDbDataSource("PostAPIDataSource", "", table)

    datasource.createResolver({
      typeName: "Query",
      fieldName: "all",
      requestMappingTemplate: MappingTemplate.dynamoDbScanTable(),
      responseMappingTemplate: MappingTemplate.dynamoDbResultList()
    })
    datasource.createResolver({
      typeName: "Mutation",
      fieldName: "save",
      requestMappingTemplate: MappingTemplate.dynamoDbPutItem(
        PrimaryKey.partition("id").is("input.id"),
        Values.projecting("input")
      ),
      responseMappingTemplate: MappingTemplate.dynamoDbResultItem()
    })
    datasource.createResolver({
      typeName: "Mutation",
      fieldName: "delete",
      requestMappingTemplate: MappingTemplate.dynamoDbDeleteItem("id", "id"),
      responseMappingTemplate: MappingTemplate.dynamoDbResultItem()
    })

    const queryMappingTemplate = `
      {
        "version": "2017-02-28",
        "operation": "Query",
        "query": {
          "expression": "#id = :id AND #createTime BETWEEN :start AND :end",
          "expressionNames": {
            "#id": "id"
            "#createTime": "create_time"
          },
          "expressionValues": {
            ":id": $util.dynamodb.toDynamoDBJson($ctx.args.id),
            ":start": $util.dynamodb.toDynamoDBJson($ctx.args.start),
            ":end": $util.dynamodb.toDynamoDBJson($ctx.args.end)
          }
        }
      }
    `
    datasource.createResolver({
      typeName: "Query",
      fieldName: "query",
      requestMappingTemplate: MappingTemplate.fromString(queryMappingTemplate),
      responseMappingTemplate: MappingTemplate.dynamoDbResultList()
    })
  }
}

我会解释下去

我們將解釋存儲在堆疊中的 TS 檔案內容。此次例子中,我們使用 GraphQL 定義了一個使用 AppSync 來讀寫 DynamoDB 資料的方案。同時,我們使用 Cognito 進行身份驗證。

除了AppSync之外的资源定义

定义Cognito和DynamoDB。

// CognitoのUserPool定義
const userPool = new UserPool(this, "UserPool", {
  userPoolName: "DemoAPIUserPool",
  signInType: SignInType.USERNAME
})

// DynamoDBのTable定義
const table = new Table(this, "CDKPostTable", {
  tableName: "CDKPostTable",
  partitionKey: {
    name: "id",
    type: AttributeType.STRING
  },
  sortKey: {
    name: "create_time",
    type: AttributeType.STRING
  },
  billingMode: BillingMode.PROVISIONED,
  readCapacity: 1,
  writeCapacity: 1
})

AppSync的定义

我們要定義AppSync的本體。

propatydescriptionnameAPI名logConfig残すログのレベルuserPoolConfigCognitoUserPoolの情報schemaDefinitionFileGraphQLのSchemaファイルのパス
const api = new GraphQLApi(this, "PostAPI", {
  name: "PostAPI",
  logConfig: {
    fieldLogLevel: FieldLogLevel.ALL
  },
  userPoolConfig: {
    userPool,
    defaultAction: UserPoolDefaultAction.ALLOW
  },
  schemaDefinitionFile: join(__dirname, "schema.graphql")
})

DataSource的定义是什么?

我们定义AppSync的DataSource。
因为之前我们定义了GraphQLAApi,并且它有一个名为addDynamoDbDataSource()的方法,所以我们可以使用它来定义DataSource。
我们将数据源名称作为第一个参数传递,将描述作为第二个参数传递,并将DynamoDB的表作为第三个参数传递。

const datasource = api.addDynamoDbDataSource("PostAPIDataSource", "", table)

Resolver的定义是什么?

在GraphQL的每个查询/变异中定义解析器。
因为之前定义的数据源具有一个名为createResolver()的方法,所以我们可以使用它来定义解析器。

propatydescriptiontypeNameQueryなのかMutationなのかfieldNameGraphQLのフィールド名requestMappingTemplateリクエスト時のマッピングテンプレートresponseMappingTemplateレスポンス時のマッピングテンプレート
datasource.createResolver({
  typeName: "Query",
  fieldName: "all",
  requestMappingTemplate: MappingTemplate.dynamoDbScanTable(),
  responseMappingTemplate: MappingTemplate.dynamoDbResultList()
})

目前提供了一种能够生成操作DynamoDB的MappingTemplate的方法,包括扫描(Scan)、获取项(GetItem)、插入项(PutItem)和删除项(DeleteItem)。

MappingTemplate.dynamoDbScanTable()
MappingTemplate.dynamoDbGetItem()
MappingTemplate.dynamoDbPutItem()
MappingTemplate.dynamoDbDeleteItem()

我认为在这里会有一些人意识到,没有查询(Query)…
在这种情况下,您需要自己编写 MappingTemplate。
如果您自己编写了 MappingTemplate,则可以使用 MappingTemplate.fromString() 进行定义。
刚才介绍的仅仅是一个方便的函数,当然您也可以像这样自己编写和定义。

const queryMappingTemplate = `
  {
    "version": "2017-02-28",
    "operation": "Query",
    "query": {
      "expression": "#id = :id AND #createTime BETWEEN :start AND :end",
      "expressionNames": {
        "#id": "id"
        "#createTime": "create_time"
      },
      "expressionValues": {
        ":id": $util.dynamodb.toDynamoDBJson($ctx.args.id),
        ":start": $util.dynamodb.toDynamoDBJson($ctx.args.start),
        ":end": $util.dynamodb.toDynamoDBJson($ctx.args.end)
      }
    }
  }
`
datasource.createResolver({
  typeName: "Query",
  fieldName: "query",
  requestMappingTemplate: MappingTemplate.fromString(queryMappingTemplate),
  responseMappingTemplate: MappingTemplate.dynamoDbResultList()
})

同时,还为响应的MappingTemplate准备了选择。
如果要返回数组,则使用dynamoDbResultList(),如果要返回单个项目,则使用dynamoDbResultItem()。

MappingTemplate.dynamoDbResultList()
MappingTemplate.dynamoDbResultItem()

整理

我个人认为,AppSync的高级构造中的Resolver定义部分最令人高兴。
由于提供了诸如MappingTemplate.dynamoDbPutItem()这样方便的函数,不再需要手写所有Resolver,代码量大大减少。
此外,在以前使用Cfnxxxx定义资源时,需要在代码中使用addDependsOn()来确保资源按顺序部署,但现在它在内部完成了这个任务,这一点也非常方便(因为我经常会忘记)。

希望大家都能尝试一下 AWSCDK!再见!!!

广告
将在 10 秒后关闭
bannerAds