なにこれ
AppSyncのリゾルバーでDynamoDBのデータを更新する場合、更新処理の前に入力チェックは必ず実施すると思います。 この時、入力チェックエラーを見つけた時点で1つのエラーメッセージを返すより、 すべてのチェック実施後にまとめてエラーメッセージを返してあげたほうが、AppSyncの呼び出し側としては助かります。 ただ、このやり方は調べてもあまり出てきませんでした。そのため本記事で紹介します。
結論
たとえば、人(ID、名前、組織名)を更新する場合
mutation {
updatePerson (input: {
id
name
organizationName
}){
person {
id
name
organizationName
}
}
}
以下のようにリクエストマッピングテンプレートのVTLを記述します。
#if($util.isNullOrBlank($ctx.args.input.name))
$util.appendError("名前は30文字以下で入力してください。", "name", null, $ctx.args.input.name)#end
#if($ctx.args.input.name.length() > 30))
$util.appendError("名前は30文字以下で入力してください。", "name", null, $ctx.args.input.name)#end
#if($util.isNullOrBlank($ctx.args.input.organizationName))
$util.appendError("組織名は30文字以下で入力してください。", "organizationName", null, $ctx.args.input.organizationName)#end
#if($ctx.args.input.organizationName.length() > 30))
$util.appendError("組織名は30文字以下で入力してください。", "organizationName", null, $ctx.args.input.organizationName)#end
#if($ctx.outErrors.size() > 0) #return($ctx.outErrors)#end
{
"operation" : "PutItem",
"key" : {
"id" : { "S" : "${ctx.args.input.id}" }
},
"condition" : {
"expression" : "attribute_exists(id)"
},
"attributeValues" : $util.dynamodb.toMapValuesJson($ctx.args.input)
}
実際どのようなレスポンスが返ってくるか
以下のmutationを発行してみると...
mutation {
updatePerson(input: {
id: "1234"
name: "あきらかに文字数オーバーの名前あきらかに文字数オーバーの名前あきらかに文字数オーバーの名前あきらかに文字数オーバーの名前"
organizationName: "あきらかに文字数オーバーの組織名あきらかに文字数オーバーの組織名あきらかに文字数オーバーの組織名あきらかに文字数オーバーの組織名"
}) {
person {
id
name
organizationName
}
}
}
複数エラーメッセージを含むレスポンスが返ってきます!
{
"data": {
"updatePerson": {
"person": null
}
},
"errors": [
{
"path": [ "updatePerson" ], "data": null, "errorType": "name", "errorInfo": "あきらかに文字数オーバーの名前あきらかに文字数オーバーの名前あきらかに文字数オーバーの名前あきらかに文字数オーバーの名前", "locations": [ { "line": 2, "column": 3, "sourceName": null } ], "message": "名前は30文字以下で入力してください。" },
{
"path": [ "updatePerson" ], "data": null, "errorType": "organizationName", "errorInfo": "あきらかに文字数オーバーの組織名あきらかに文字数オーバーの組織名あきらかに文字数オーバーの組織名あきらかに文字数オーバーの組織名", "locations": [ { "line": 2, "column": 3, "sourceName": null } ], "message": "組織名は30文字以下で入力してください。" }
]
}
結論に至るまでの試行錯誤
AppSyncの公式ドキュメントを見ると、$util.appendError
が使えそうだと思ったのですが、いざ書こうとすると、以下2点につまづきました。
- 「1つ以上入力チェックエラーがある」ことをどうやって判定するのか
- 複数エラー情報をどうやってレスポンスとして返すのか
1. 「1つ以上入力チェックエラーがある」ことをどうやって判定するのか
最初に考えたのは、#set($valid=true)
のようにフラグで判断する方法です。
## 事前にフラグを定義しておく#set($valid = true)
#if($util.isNullOrBlank($ctx.args.input.name))
## チェックNGの場合はフラグを更新 #set($valid=false) $util.appendError("名前は30文字以下で入力してください。", "name", null, $ctx.args.input.name)
#end
#if($ctx.args.input.name.length() > 30))
#set($valid=false) $util.appendError("名前は30文字以下で入力してください。", "name", null, $ctx.args.input.name)
#end
#if($util.isNullOrBlank($ctx.args.input.organizationName))
#set($valid=false) $util.appendError("組織名は30文字以下で入力してください。", "organizationName", null, $ctx.args.input.organizationName)
#end
#if($ctx.args.input.organizationName.length() > 30))
#set($valid=false) $util.appendError("組織名は30文字以下で入力してください。", "organizationName", null, $ctx.args.input.organizationName)
#end
#if($valid == false) ## 異常時の処理#end
ただこれだと記述が冗長です。
appendErrorしたエラーはどこに格納されているのか?
$util.error($util.toJson($context))
をして$context
の中身を確認してみると、
どうやら$context.outErrors
に格納されているということがわかりました。
それを踏まえて処理を以下のようにリファクタリングしました。
#if($util.isNullOrBlank($ctx.args.input.name))
$util.appendError("名前は30文字以下で入力してください。", "name", null, $ctx.args.input.name)
#end
#if($ctx.args.input.name.length() > 30))
$util.appendError("名前は30文字以下で入力してください。", "name", null, $ctx.args.input.name)
#end
#if($util.isNullOrBlank($ctx.args.input.organizationName))
$util.appendError("組織名は30文字以下で入力してください。", "organizationName", null, $ctx.args.input.organizationName)
#end
#if($ctx.args.input.organizationName.length() > 30))
$util.appendError("組織名は30文字以下で入力してください。", "organizationName", null, $ctx.args.input.organizationName)
#end
#if($ctx.outErrors.size() > 0) ## 異常時の処理#end
2. 複数エラー情報をどうやってレスポンスとして返すのか
$util.appendError
はエラーを追加してくれるだけです。$util.error
は1つのエラー情報しか返してくれません。
試行錯誤した結果、#return
で返せそうだということがわかりました。
1の調査結果により$util.appendError
で詰めたエラー情報は$ctx.outErrors
に格納されていることがわかっていたので、
それら踏まえて以下のようにしました。
#return($ctx.outErrors)
まとめ
VLTは結構めんどくさいですが、ちょっとずつ慣れてきました。 ググってもAppSyncのVTL系の情報は、まだあまり見かけません。そのため今後もノウハウがたまったら記事を書きます🍅