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的本體。
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()的方法,所以我们可以使用它来定义解析器。
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!再见!!!