AWS Amplify で複数件登録できるカスタムリゾルバーを追加する

記事タイトルとURLをコピーする

こんにちは。アプリケーションサービス部 河野です。

AWS Amplify(以下 Amplify) は CRUD 処理を含んだリゾルバーを自動的に生成することができますが、 それ以外の処理を実現したい場合は、カスタムリゾルバーを追加する必要があります。
例えば、一回のリクエストで複数のテーブルを更新したい、複数のデータを処理したいなどといった場合です。

今回は、デフォルトで作成したスキーマに、複数件登録できるカスタムリゾルバーを追加します。

前提

amplify init 後に以下スキーマが追加されている状態です。
amplify add api でデフォルトスキーマを作成した状態)

type Todo @model {
    id: ID!
    name: String!
    description: String
}

やりたいこと

一回のリクエストで、 Todo テーブルに複数件登録できることを目指します。
@model で宣言されたリゾルバーを使用すれば、createTodo(input: {name: "hoge"}) といった形でデータを登録できます。

やりたいことは、createBatchTodo(input: [{name: "hoge"}, {name: "fuga"}])といった形で、引数にリストを渡して、複数件登録することです。

実装

Mutation の追加

まずは、schema.graphql に複数件登録する Mutation を追加します。

input TodoInput {
    id: ID
    name: String!
    description: String
}

type Mutation {
    batchAddTodo(todos: [TodoInput]): [Todo]
    batchDeleteTodo(ids: [ID]): [Todo]
}

batchAddTodo は、TodoInput のリストを引数に持ちます。ID はリゾルバー側で自動生成させるため、必須にしていません。 batchDeleteTodo は ID リストを引数にもち、一致した ID を一括削除します。

カスタムリゾルバーの作成

カスタムリゾルバーは、amplify/backend/api/{your_api_name}/resolvers に追加します。 以下リゾルバーを作成します。

  • Mutation.batchAddTodo.req.vtl
  • Mutation.batchAddTodo.res.vtl
  • Mutation.batchDeleteTodo.res.vtl
  • Mutation.batchDeleteTodo.res.vtl

Mutation.batchAddTodo.req.vtl

batchAddTodo 実行時のリクエストリゾルバーを定義します。

#set($data = [])
#set( $createdAt = $util.time.nowISO8601() )
#foreach($item in ${ctx.args.todos})
    $util.qr($item.put("id", $util.defaultIfNullOrBlank($item.id, $util.autoId())))
    $util.qr($item.put("createdAt", $util.defaultIfNull($item.createdAt, $createdAt)))
    $util.qr($item.put("updatedAt", $util.defaultIfNull($item.updatedAt, $createdAt)))
    $util.qr($data.add($util.dynamodb.toMapValues($item)))
#end

{
    "version" : "2018-05-29",
    "operation" : "BatchPutItem",
    "tables" : {
        "Todo-xxxxxxxxxxx-dev": $util.toJson($data)
    }
}

引数で渡ってきたリストを、DynamoDB の BatchPutItem のパラメータを生成しているイメージです。 生成時に、id, createdAt, updatedAt を付与しています。

tables で指定している テーブル名は AppSync のデータソースから参照できます。

f:id:swx-go-kawano:20210605230008p:plain

BatchPutItem を使用した複数件登録については、以下チュートリアルに記載されておりますのでご参考ください。

docs.aws.amazon.com

Mutation.batchAddTodo.res.vtl

batchAddTodo 実行時のレスポンスリゾルバーを定義します。

#if( $ctx.error )
$util.error($ctx.error.message, $ctx.error.type)
#else
$util.toJson($ctx.result.data.Todo-xxxxxxxxxxx-dev)
#end

レスポンスは特に手を加えずに、DynamoDB から取得したデータををそのまま返します。

Mutation.batchDeleteTodo.req.vtl

#set($ids = [])
#foreach($id in ${ctx.args.ids})
    #set($map = {})
    $util.qr($map.put("id", $util.dynamodb.toString($id)))
    $util.qr($ids.add($map))
#end

{
    "version" : "2018-05-29",
    "operation" : "BatchDeleteItem",
    "tables" : {
        "Todo-xxxxxxxxxxx-dev": $util.toJson($ids)
    }
}

batchAddTodoと考え方は同じで、リストで受け取った id をもとに、BatchDeleteItem のパラメータを作成しています。

Mutation.batchDeleteTodo.res.vtl

#if( $ctx.error )
$util.error($ctx.error.message, $ctx.error.type)
#else
$util.toJson($ctx.result.data.Todo-xxxxxxxxxxx-dev)
#end

batchAddTodoと同様、特に手を加えずに、DynamoDB から取得したデータををそのまま返します。

マッピングリソース作成

Amplify でカスタムリゾルバーをデプロイするためには、amplify/backend/api/{your_api_name}/stacks/CustomResource.jsonに追記する必要があります。

以下は、新しくスキーマに追加した Mutation に 先ほど定義したカスタムリゾルバーをマッピングする CloudFormation を定義しています。

"Resources": {
    "EmptyResource": {
      "Type": "Custom::EmptyResource",
      "Condition": "AlwaysFalse"
    },
    "batchAddTodoResolver": {
        "Type": "AWS::AppSync::Resolver",
        "Properties": {
          "ApiId": {
            "Ref": "AppSyncApiId"
          },
          "DataSourceName": "TodoTable",
          "TypeName": "Mutation",
          "FieldName": "batchAddTodo",
          "RequestMappingTemplateS3Location": {
            "Fn::Sub": [
              "s3://${S3DeploymentBucket}/${S3DeploymentRootKey}/resolvers/Mutation.batchAddTodo.req.vtl",
              {
                "S3DeploymentBucket": {
                  "Ref": "S3DeploymentBucket"
                },
                "S3DeploymentRootKey": {
                  "Ref": "S3DeploymentRootKey"
                }
              }
            ]
          },
          "ResponseMappingTemplateS3Location": {
            "Fn::Sub": [
              "s3://${S3DeploymentBucket}/${S3DeploymentRootKey}/resolvers/Mutation.batchAddTodo.res.vtl",
              {
                "S3DeploymentBucket": {
                  "Ref": "S3DeploymentBucket"
                },
                "S3DeploymentRootKey": {
                  "Ref": "S3DeploymentRootKey"
                }
              }
            ]
          }
        }
      },
      "batchDeleteTodoResolver": {
        "Type": "AWS::AppSync::Resolver",
        "Properties": {
          "ApiId": {
            "Ref": "AppSyncApiId"
          },
          "DataSourceName": "TodoTable",
          "TypeName": "Mutation",
          "FieldName": "batchDeleteTodo",
          "RequestMappingTemplateS3Location": {
            "Fn::Sub": [
              "s3://${S3DeploymentBucket}/${S3DeploymentRootKey}/resolvers/Mutation.batchDeleteTodo.req.vtl",
              {
                "S3DeploymentBucket": {
                  "Ref": "S3DeploymentBucket"
                },
                "S3DeploymentRootKey": {
                  "Ref": "S3DeploymentRootKey"
                }
              }
            ]
          },
          "ResponseMappingTemplateS3Location": {
            "Fn::Sub": [
              "s3://${S3DeploymentBucket}/${S3DeploymentRootKey}/resolvers/Mutation.batchDeleteTodo.res.vtl",
              {
                "S3DeploymentBucket": {
                  "Ref": "S3DeploymentBucket"
                },
                "S3DeploymentRootKey": {
                  "Ref": "S3DeploymentRootKey"
                }
              }
            ]
          }
        }
      }
   }

デプロイ

以下コマンドでAWS環境にデプロイします。

amplify push -y

動作確認

想定通りの挙動になっているか、appsync のクエリ画面から実行してみます。

複数追加

f:id:swx-go-kawano:20210605231027p:plain

f:id:swx-go-kawano:20210605231045p:plain

複数削除

f:id:swx-go-kawano:20210605231200p:plain

f:id:swx-go-kawano:20210605231527p:plain

終わりに

今回は、Mutation のカスタムリゾルバーを作成しましたが、更に複雑な処理をしたい場合は、 クエリーから、AWS Lambda を呼び出すといったことも可能です。
今後もいろいろ試していきたいと思います。