はじめに
アドベントカレンダーに初参加させていただきます。
トライ&エラーで進めている部分あるので、誤りや不足がある場合はご指摘よろしくお願いします。
Amplifyとの出会い
元々はAWS Elastic Beanstalk(+RDS+CakePHP)を使って開発していました。
今年9月からの個人的な開発でReactを使い始めた際に、バックエンドどうしよう、認証を自前で作りたくないなと思っていたところ、Coginitoも使えるAmplify良さそうと思い、使い始めました。
AppSyncもDynamoもAmplifyで初めて使うので、問題発生時に「どこの問題なのかわからない状態」に陥ったりしますが、バックエンド含めてソースコードで管理できるのは便利だなと思い、楽しく使わせてもらっています。
今回やりたかったこと
ReactからAppSyncを呼び出して、Dynamoの複数テーブル、複数レコードを1つのトランザクションで更新したい。
結論
カスタムリゾルバで作成しました。
マニュアル
ドキュメントはQueryのカスタムリゾルバですが、Mutationもほとんど同じでできました。
以下マニュアルの手順同様にMutationの場合にどうしたかを記載します。
対応内容
schema.graphqlにMutationを追加
今回はシンプルにAaa
テーブルとBbb
テーブルを用意して、それぞれに対する複数行の更新処理を1つのトランザクションで更新するイメージです。
createAaaAndBbb
Mutationを追加して、それに必要なAaaInput
,BbbInput
,AaasAndBbbs
を追加します。
ちなみにこの方法だとAaa
とAaaInput
の項目を常に合わせる必要があり、不便ですがもっといい方法があるのかも。。
type Aaa @model{
id: ID
name: String!
}
type Bbb @model {
id: ID
name: String!
}
input AaaInput {
id: ID
name: String!
}
input BbbInput {
id: ID
name: String!
}
type AaasAndBbbs{
aaas: [Aaa]
bbbs: [Bbb]
}
type Mutation {
createAaasAndBbbs(
aaas: [AaaInput]
bbbs: [BbbInput]
): AaasAndBbbs
}
リゾルバーリソースをスタックに追加
./amplify/backend/api/[project]/stacks/AaaAndBbbResources.json
を作成します。
デフォルトで配置されていたCustomResources.json
をコピーして「//変更」と記載した部分のみ修正しました。
{
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "An auto-generated nested stack.",
"Metadata": {},
"Parameters": {
"AppSyncApiId": {
"Type": "String",
"Description": "The id of the AppSync API associated with this project."
},
"AppSyncApiName": {
"Type": "String",
"Description": "The name of the AppSync API",
"Default": "AppSyncSimpleTransform"
},
"env": {
"Type": "String",
"Description": "The environment name. e.g. Dev, Test, or Production",
"Default": "NONE"
},
"S3DeploymentBucket": {
"Type": "String",
"Description": "The S3 bucket containing all deployment assets for the project."
},
"S3DeploymentRootKey": {
"Type": "String",
"Description": "An S3 key relative to the S3DeploymentBucket that points to the root\nof the deployment directory."
}
},
"Resources": {
"CreateAaasAndBbbsResolver": { //変更
"Type": "AWS::AppSync::Resolver",
"Properties": {
"ApiId": {
"Ref": "AppSyncApiId"
},
"DataSourceName": "AaaTable",
"TypeName": "Mutation", //変更
"FieldName": "createAaasAndBbbs", //変更
"RequestMappingTemplateS3Location": {
"Fn::Sub": [
"s3://${S3DeploymentBucket}/${S3DeploymentRootKey}/resolvers/Mutation.createAaasAndBbbs.req.vtl", //変更
{
"S3DeploymentBucket": {
"Ref": "S3DeploymentBucket"
},
"S3DeploymentRootKey": {
"Ref": "S3DeploymentRootKey"
}
}
]
},
"ResponseMappingTemplateS3Location": {
"Fn::Sub": [
"s3://${S3DeploymentBucket}/${S3DeploymentRootKey}/resolvers/Mutation.createAaasAndBbbs.res.vtl", //変更
{
"S3DeploymentBucket": {
"Ref": "S3DeploymentBucket"
},
"S3DeploymentRootKey": {
"Ref": "S3DeploymentRootKey"
}
}
]
}
}
}
},
"Conditions": {
"HasEnvironmentParameter": {
"Fn::Not": [
{
"Fn::Equals": [
{
"Ref": "env"
},
"NONE"
]
}
]
},
"AlwaysFalse": {
"Fn::Equals": ["true", "false"]
}
},
"Outputs": {
"EmptyOutput": {
"Description": "An empty output. You may delete this if you have at least one resource above.",
"Value": ""
}
}
}
リゾルバーテンプレートの作成
./amplify/backend/api/[project]/resolvers/
にMutation.createAaasAndBbbs.req.vtl
とMutation.createAaasAndBbbs.res.vtl
を作成します。
トランザクションはTransactWriteItems
を利用することで実現できます。
ただここですごい気になっているのが、Aaa-xxxxxxxxxxxxxxxxxxxxxxxxxx-dev
の部分。
-xxxxxxxxxxxxxxxxxxxxxxxxxx-dev
がDynamoDBのテーブル名に自動で振られるのだけど、vtl上で取得する方法がわかりませんでした。
このままだと別環境へのリリースとかできないんじゃ・・・。
#set($aaaTransactPutItems = [])
#set($index = 0)
#foreach($aaa in ${ctx.args.aaas})
#set($keyMap = {})
$util.qr($keyMap.put("id", $util.dynamodb.toDynamoDB($util.autoId())))
#set($attributeValues = {})
$util.qr($attributeValues.put("name", $util.dynamodb.toDynamoDB($aaa.name)))
#set($index = $index + 1)
#set($aaaTransactPutItem = {"table": "Aaa-xxxxxxxxxxxxxxxxxxxxxxxxxx-dev",
"operation": "PutItem",
"key": $keyMap,
"attributeValues": $attributeValues})
$util.qr($aaaTransactPutItems.add($aaaTransactPutItem))
#end
#set($bbbTransactPutItems = [])
#set($index = 0)
#foreach($bbb in ${ctx.args.bbbs})
#set($keyMap = {})
$util.qr($keyMap.put("id", $util.dynamodb.toDynamoDB($util.autoId())))
#set($attributeValues = {})
$util.qr($attributeValues.put("name", $util.dynamodb.toDynamoDB($bbb.name)))
#set($index = $index + 1)
#set($bbbTransactPutItem = {"table": "Bbb-xxxxxxxxxxxxxxxxxxxxxxxxxx-dev",
"operation": "PutItem",
"key": $keyMap,
"attributeValues": $attributeValues})
$util.qr($bbbTransactPutItems.add($bbbTransactPutItem))
#end
#set($transactItems = [])
$util.qr($transactItems.addAll($aaaTransactPutItems))
$util.qr($transactItems.addAll($bbbTransactPutItems))
{
"version" : "2018-05-29",
"operation" : "TransactWriteItems",
"transactItems" : $util.toJson($transactItems)
}
さいごに
上記でとりあえずトランザクション処理が実現できることまでは確認できました。
でももっとスマートな記述ができるのでは思っています。。
調査不足にもかかわらずアドベントカレンダーに手をあげてしまいましたが、この機会に自分でも上記追加調査してみようと思います。