AWS AmplifyのAPI入力バリデーション実現方法

2 min read読了の目安(約2600字

この記事は何

  • Amplify CLIを使うと簡単にAppSync+DynamoDBを使ったGraphQL APIを作成可能です。(公式リンク
  • 開発者が下記のようなschemaを定義することで、Amplify CLIはDynamoDBのテーブルや、AppSyncのリゾルバ(query, mutation, subscribe用)を生成してくれます。
schemaの例
type Blog @model {
  id: ID!
  name: String!
  posts: [Post] @connection(name: "BlogPosts")
}
type Post @model {
  id: ID!
  title: String!
  blog: Blog @connection(name: "BlogPosts")
  comments: [Comment] @connection(name: "PostComments")
}
type Comment @model {
  id: ID!
  content: String
  post: Post @connection(name: "PostComments")
}
  • 上記の例中にある@model@connectionをディレクティブと呼び、これによってschemaからどのようにリゾルバを生成するかコントロールしています(ざっくり)。
  • Issueも出て議論されているのでそのうち実装されそうですが、現状は入力に対するバリデーションを定義するディレクティブはありませんこちらの開発をする時に、ユーザ入力をDynamoDBに書き込む必要があったのですが、「あれ、バリデーションが無いとか怖すぎない…?」となりました。
  • したがって、一度Amplify CLIが生成した.vtlファイルを直接編集する必要があります[1]。その方法を備忘録として残します。

前提

  • Amplify CLI 3.3.26

問題設定

次のようなschemaを定義した時に、idの長さに制限を設けたいとします。(例えばブランクは禁止かつ50文字未満とする)

schema.graphql
type Fav @model
  @key(fields:["owner", "id"])
  owner: ID!
  id: ID!
}

手順

(amplify apiの追加は事前に済んでいるものとして…)

  1. amplify pushを実行してリゾルバを生成する
  2. 生成されたリゾルバのうち、バリデーションの追加が必要な処理用のリゾルバ(例えばCreateFavなど)をコピーして指定場所に保存する。
リゾルバの生成場所
ProjectRoot
  |- amplify
      |- backend
          |- api
	      |- yourApiName
	          |- build
		  |   |- resolvers  --- ※ここに生成される
		  |
		  |- resolvers -------- ※ここにコピーして保存する

2の手順で指定したフォルダに.vtlファイルを保存すると、自動生成したリゾルバを上書きすることになる。

そのため、もし途中でデータ構造などを変えた場合には古いリゾルバを削除すること

  1. コピーしたファイルを次のように編集する。正直VTLの記述方法が分からないので、挿入場所は手探り感がありますが、自動生成されたコメントを手がかりに、primary keyの付与前にバリデーションを実施するように、次のような位置に挿入しています。
(略)
+ ## Custom for input validation
+ #if($util.isNullOrBlank($ctx.args.input.id))
+   $util.appendError("id must exist", "id", null, $ctx.args.input.id)
+ #end 
+ #if($ctx.args.input.id.length() > 50))
+   $util.appendError("The request would not be proper", "id", null, ctx.args.input.id)
+ #end

+ #if($ctx.outErrors.size() > 0)
+   #return($ctx.outErrors)
+ #end

## [Start] Set the primary @key. **
#set( $modelObjectKey = {
  "owner": $util.dynamodb.toDynamoDB($ctx.args.input.owner),
  "id": $util.dynamodb.toDynamoDB($ctx.args.input.id)
} )
## [End] Set the primary @key. **

## [Start] Prepare DynamoDB PutItem Request. **
(略)
  1. ファイルを上書き保存した後、再度amplify pushする。

おわりに

お疲れさまでした。設定方法は以上となります。念のためAppSyncコンソールなどで試しにクエリを行い、狙い通りにバリデーションが機能しているか確認してみて下さい。

脚注
  1. カスタムディレクティブを作る方法もあり、Amplifyのドキュメントで解説されていますが、私はそこに着手するガッツはありませんでした…。 ↩︎