使用AWS CDK Python构建AWS AppSync API
首先
我在以下文章中研究了使用 AWS CDK Python 进行部署的方法。本次记录了使用 AWS CDK Python 实际部署 AWS AppSync 的方法。
有关AWS CDK Python的设置方法,请参考上述文章。
执行环境等
-
- Ubuntu 20.04 LTS
-
- aws-cdk: 1.125.0 (build 67b4921)
-
- pipenv: version 2021.5.29
- デプロイ先: ap-northeast-1 (Tokyo) リージョン
部署 AWS AppSync
本文介绍如何部署以下简单的GraphQL API架构。
type User {
name: String!
age: Int!
}
type Query {
hello: String
getUsers: [ User! ]
}
type Mutation {
addUser(name: String!, age: Int!): User
}
准备好
为了将DynamoDB用作GraphQL API的数据源,并将AWS Lambda用作返回固定字符串的数据源,因此需要在Python中安装各个服务的库以使用它们。
$ pipenv install aws-cdk.aws-appsync aws-cdk.aws-dynamodb aws-cdk.aws-lambda
AWS AppSync 服务的结构
在写CDK代码之前,先简要解释一下AppSync的结构。因为在编写代码时了解结构,可以更容易理解。主要内容包括Schema、DataSource和Resolvers三部分,它们都位于GraphQL API之下。
GraphQL API: トップレベルコンテンツであり、エンドポイントを持つ
Schema: GraphQL API に紐づくスキーマ定義
DataSource: AWS AppSyncが連携する (AppSync外の) サービスの紐づけの定義。 例えば、どのような DynamoDB Table を利用するか、など
Resolvers: Schema と DataSource を結びつけるルール定義。 Schema の入力を DataSource で定義されている各サービスで利用できる形に変換してやる必要があるため、この変換・対応を実現する部分

这幅图是从AWS官方引用来的,但是从这个图中可以看出,DataSource可以独立于AppSync存在。AppSync只是作为获取和操作这些数据源的外部服务存在。
AppSync Stack 的实现
在使用cdk init app进行初始化后,我编辑和添加了以下内容。带有(*)的内容是我新添加的。
在使用cdk init app进行初始化后,我编辑和添加了以下内容。带有(*)的是我新增加的。
使用cdk init app进行初始化后,我编辑和添加了以下内容。有(*)标记的是我新增加的。
.
├── cdk_appsync
│ └── cdk_appsync_stack.py
├── lambda (*)
│ └── fixed_string.py (*)
├── resolvers (*)
│ └── addUser.vtl (*)
├── schema.graphql (*)
模式.graphql
这里是对所创建的 GraphQL API 的模式定义进行记录。
由于这与之前的模式文件相同,所以可以省略。
lambda/fixed_string.py => lambda/fixed_string.py
这是一个用作AppSync数据源的Lambda函数,返回固定的字符串。
def handler(event, context):
return 'This is a fixed string!'
添加用户的解析器功能模板文件:resolvers/addUser.vtl
AppSync的解析器可以使用Apache Velocity模板语言(VTL)进行编写。后文将介绍,虽然可以使用CDK中提供的助手来编写解析器,但这里为了展示使用VTL的示例而做准备。
内容如下:
{
"version" : "2017-02-28",
"operation" : "PutItem",
"key" : {
"name": $util.dynamodb.toDynamoDBJson($ctx.args.name)
},
"attributeValues" : {
"age": $util.dynamodb.toDynamoDBJson($ctx.args.age)
}
}
这是一个实施 DynamoDB 映射模板并实现 putItem 功能的例子。还可以适用于其他 DynamoDB 的不同操作(如 GetItem 或 ListItem)。请参考本文来了解具体的写法。
cdk_appsync/cdk_appsync_stack.py 可以被改写成中文为:cdk_appsync/cdk_appsync_stack.py
这里是正文。请参考以下页面等进行记录。
#!/usr/bin/python
# -*- coding: utf-8 -*-
from aws_cdk import (
core as cdk,
aws_lambda as cdk_lambda,
aws_appsync as cdk_appsync,
aws_dynamodb as cdk_dynamodb
)
from aws_cdk.aws_appsync import (
AuthorizationConfig, AuthorizationMode, AuthorizationType,
MappingTemplate
)
class CdkAppSyncStack(cdk.Stack):
def __init__(self, scope: cdk.Construct,
construct_id: str, **kwargs) -> None:
super().__init__(scope, construct_id, **kwargs)
# GraphQL API と利用するスキーマの定義
graph_api = cdk_appsync.GraphqlApi(
self, 'HelloAppSyncFromCDK',
name='HelloAppSyncFromCDK',
authorization_config=AuthorizationConfig(
default_authorization=AuthorizationMode(
authorization_type=AuthorizationType.API_KEY
)
),
schema=cdk_appsync.Schema.from_asset('schema.graphql'),
xray_enabled=False
)
# DynamoDB DataSource を追加
data_table = cdk_dynamodb.Table(
self, "DemoTable",
partition_key=cdk_dynamodb.Attribute(
name="name",
type=cdk_dynamodb.AttributeType.STRING
)
)
data_source = graph_api.add_dynamo_db_data_source(
'demoDataSource', data_table)
# getUsers に全件取得の Resolver を付与 (プログラムベース)
data_source.create_resolver(
type_name='Query',
field_name='getUsers',
request_mapping_template=MappingTemplate.dynamo_db_scan_table(),
response_mapping_template=MappingTemplate.dynamo_db_result_list()
)
# addUser に vtl テンプレートの Resolver を付与 (VTLベース)
data_source.create_resolver(
type_name='Mutation',
field_name='addUser',
request_mapping_template=MappingTemplate.from_file(
'resolvers/addUser.vtl'
),
response_mapping_template=MappingTemplate.dynamo_db_result_item()
)
# hello に固定文字列を返す Lambda 関数の Resolver を付与
fixed_str = cdk_lambda.Function(
self, 'FixedStringLambda',
runtime=cdk_lambda.Runtime.PYTHON_3_8,
code=cdk_lambda.Code.asset('lambda'),
handler='fixed_string.handler',
environment={},
)
graph_api.add_lambda_data_source(
'FixedStringFn', fixed_str
).create_resolver(
type_name='Query', field_name='hello'
)
通过代码,我们可以清楚地看出以下特点:”数据源的外部资源定义(如 data_table 和 fixed_str)”,”与外部资源的关联(add_xxxxxxx_data_source)”,”与解析器的对应关系定义(create_resolver)”都被明确地分开了。我认为,如果我们理解了AppSync的结构和定位,这些就会迅速地融入我们的思维。
如果实施到这一步,可以使用pipenv run cdk deploy进行部署。
部署后的操作验证
尝试使用curl进行操作验证。由于这次进行了基于API密钥的安全措施,所以我们需要从AppScyn控制台获取终端点URL和API密钥来使用。
# 固定文字列を取得するクエリ: hello を発行
$ curl -s -X POST -H "Context-Type: application/json" /
-H "X-API-Key: ************************************" \
-d '{"query": "{ hello }"}' \
https://**************************.appsync-api.ap-northeast-1.amazonaws.com/graphql | jq .
{
"data": {
"hello": "This is a fixed string!"
}
}
# ユーザーを追加する mutation
$ curl -s -X POST -H "Context-Type: application/json" /
-H "X-API-Key: ************************************" \
-d '{"query": "mutation { addUser(name: \"TestUser1\", age: 20) { name age } }"}' \
https://**************************.appsync-api.ap-northeast-1.amazonaws.com/graphql | jq .
{
"data": {
"addUser": {
"name": "TestUser1",
"age": 20
}
}
}
# 全ユーザー取得するクエリ
$ curl -s -X POST -H "Context-Type: application/json" /
-H "X-API-Key: ************************************" \
-d '{"query": "{ getUsers { name age } }"}' \
https://**************************.appsync-api.ap-northeast-1.amazonaws.com/graphql | jq .
{
"data": {
"getUsers": [
{
"name": "TestUser1",
"age": 20
}
]
}
}
# --------
# 再度ユーザーを追加
$ curl -s -X POST -H "Context-Type: application/json" /
-H "X-API-Key: ************************************" \
-d '{"query": "mutation { addUser(name: \"TestUser2\", age: 30) { name age } }"}' \
https://**************************.appsync-api.ap-northeast-1.amazonaws.com/graphql | jq .
{
"data": {
"addUser": {
"name": "TestUser2",
"age": 30
}
}
}
# 全ユーザー取得するクエリ
$ curl -s -X POST -H "Context-Type: application/json" /
-H "X-API-Key: ************************************" \
-d '{"query": "{ getUsers { name age } }"}' \
https://**************************.appsync-api.ap-northeast-1.amazonaws.com/graphql | jq .
{
"data": {
"getUsers": [
{
"name": "TestUser1",
"age": 20
},
{
"name": "TestUser2",
"age": 30
}
]
}
}
另外,还能确认数据已保存在DynamoDB表中。

总结
我在這裡使用AWS CDK Python來定義並部署了一個簡單的AppSync API。同時,我也確認了使用端點進行的操作。基本上,如果基於AWS Management Console,它提供了一些常用的模板自動生成功能,甚至可以從模式定義中自動生成數據源和解析器等功能。這樣,我可以在控制台上輕鬆手動創建資源,同時將必要的部分切割並轉移到AWS CDK中,使工作更順利進行。