AppSync とは
AppSync は AWS のマネージドな GraphQL サービスであり、GraphQL API を作成するのに必要なバックエンドのリソースを提供してくれます。AppSync を使うことでユーザーがやらなければいけないことは以下のような点に絞られます。
- スキーマの定義
- リゾルバーの作成
- データソースの作成
なお、リゾルバーは Lambda 関数を用いてカスタマイズすることが可能ですが、その他にも DynamoDB 、RDS 、OpenSearch といった AWS サービスに直接接続することもできるため、データハンドルが容易になります。
今回は AppSync の DynamoDB リゾルバーを用いて、前回 作成した ToDo アプリ用の GraphQL API を作っていきたいと思います。
AppSync API の作成
まずは AppSync API を作成していきます。AppSync コンソールから API を作成 ボタンを押し、Design from scratch を選択します。API 名等必要な情報を入力し、作成します。
スキーマの作成
API の作成が完了したら、次にスキーマを作成します。ToDo アプリで利用したものをそのままこちらでも使うので、以下のスキーマを利用します。
schema {
query: Query
mutation: Mutation
}
type Query {
listTodos: [Todo!]!
}
type Mutation {
createTodo(data: CreateTodoInput!): Todo!
updateTodo(id: ID!, data: UpdateTodoInput!): Todo!
deleteTodo(id: ID!): Todo!
}
input CreateTodoInput {
title: String!
description: String!
createDate: String!
}
input UpdateTodoInput {
title: String!
description: String!
createDate: String!
}
type Todo {
id: ID!
title: String!
description: String!
createDate: String!
}
AppSync コンソールから先に作成した API を選択し、左ナビゲーションペインより スキーマ を選択します。以下の通りスキーマを入力し、スキーマを保存 ボタンを押せば完了です。
データソースの作成
次に DynamoDB テーブル用のデータソースを作成します。これにより AppSync サービスがどのテーブルを参照すれば良いか判断ができるようになります。
左ナビゲーションペインの Data sources を選択し、データソースを作成 をクリックします。必要な情報を入力し、作成 を押して完了です。
ちなみに、新規でロールを作成すると以下のようなポリシーがアタッチされます。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"dynamodb:DeleteItem",
"dynamodb:GetItem",
"dynamodb:PutItem",
"dynamodb:Query",
"dynamodb:Scan",
"dynamodb:UpdateItem"
],
"Resource": [
"arn:aws:dynamodb:region:account-id:table/your-table-name",
"arn:aws:dynamodb:region:account-id:table/your-table-name/*"
]
}
]
}
リゾルバーの作成
それではいよいよリゾルバーを作成していきます。リゾルバーの作成はスキーマ定義画面にあるフィールド横の アタッチ から行います。
AppSync のリゾルバーはリクエスト用及びレスポンス用のマッピングテンプレートで構成されます。これらにより、AppSync が受信したリクエストをバックエンドのデータソースへの指示に変換する方法と、そのデータソースからのレスポンスを GraphQL レスポンスへ、つまりクライアントが受け取るレスポンスへ変換する方法が提供されます。
また、AppSync のリゾルバーにはユニットリゾルバーとパイプラインリゾルバーの 2 種類があります。ユニットリゾルバーは単一のリクエストマッピングテンプレートとレスポンスマッピングテンプレートのみで構成されます。一方で、パイプラインリゾルバーでは単一のリクエストに対していくつかの関数を実行し、最終的にレスポンスを返すというような構成が取れます。これにより、シンプルなマッピングのみでは解決できない複雑なロジックを含むリゾルバーを作成できます。なお、今回はユニットリゾルバーを使用します。
createTodo
まずは createTodo 用のリゾルバーを作成します。アタッチ をクリックします。するとデフォルトでパイプラインリゾルバー作成画面が表示されるので、右上の アクション から ランタイムを更新 を選択し、リゾルバータイプを Unit Resolver (VTL only) へ変更します。
データソースを選択する画面になるので、先に作成したデータソースを選択し、リクエストマッピングテンプレート及びレスポンスマッピングテンプレートを以下のように入力します。
{
"version" : "2017-02-28",
"operation" : "PutItem",
"key" : {
"id" : $util.dynamodb.toDynamoDBJson($util.autoId())
},
"attributeValues" : {
"title" : $util.dynamodb.toDynamoDBJson($ctx.args.data.title),
"description" : $util.dynamodb.toDynamoDBJson($ctx.args.data.description),
"createDate" : $util.dynamodb.toDynamoDBJson($ctx.args.data.createDate)
}
}
$util.dynamodb.toMapValuesJson($ctx.args.data)
$util.toJson($ctx.result)
それでは動作確認をしてみましょう。左ナビゲーションペインから クエリ を選択し、以下のクエリを実行します。正しくレスポンスが得られれば成功です。
mutation CreateTodo {
createTodo(data: {createDate: "2023-05-06", description: "From AppSync", title: "From AppSync"}) {
title
id
description
createDate
}
}
DynamoDB テーブル側にもデータが追加されたことがわかります。
listTodos
次に listTodos 用のリゾルバーです。先と同様にユニットリゾルバーを以下のリクエスト・レスポンスマッピングテンプレートで作成します。
{
"version" : "2017-02-28",
"operation" : "Scan"
}
$utils.toJson($ctx.result.items)
それでは実際に実行してみましょう。
ちゃんとリストも取得できてそうですね。
updateTodo
updateTodo も同様に行います。
{
"version" : "2017-02-28",
"operation" : "UpdateItem",
"key" : {
"id" : $util.dynamodb.toDynamoDBJson($ctx.args.id)
},
"update" : {
"expression" : "SET title = :title, description = :description, createDate = :createDate",
"expressionValues": {
":title" : $util.dynamodb.toDynamoDBJson($ctx.args.data.title),
":description" : $util.dynamodb.toDynamoDBJson($ctx.arguments.data.description),
":createDate" : $util.dynamodb.toDynamoDBJson($ctx.args.data.createDate)
}
}
}
$util.toJson($ctx.result)
実際に動くところも確認しましょう。
deleteTodo
最後は deleteTodo です。
{
"version" : "2017-02-28",
"operation" : "DeleteItem",
"key" : {
"id" : $util.dynamodb.toDynamoDBJson($ctx.args.id)
}
}
$util.toJson($ctx.result)
実際にクエリ実行画面から削除してみます。
listTodos で実際に消されていることも確認できます。
以上でリゾルバーの作成は完了です。
エンドポイントを利用してみる
最後に AppSync から払い出される API エンドポイントを利用して動作の確認をしてみます。
$ curl -g \
-X POST \
-H "Content-Type: application/json" \
-H "x-api-key: your-api-key" \
-d '{"query": "query {listTodos {id title description createDate}}"}' https://your-api-id.appsync-api.us-west-2.amazonaws.com/graphql
{"data":{"listTodos":[{"id":"8295a0c5-81a0-40e2-97d1-89bd66041bf3","title":"Ask Cathy for the case update","description":"Haven't received any update on my L1 extension case for a while. Check with Cathy for the update. ","createDate":"2023-05-06"},{"id":"0386490d-23a8-4fc3-9009-646e23a9c234","title":"Pick up rice \"Tsuyahime\"","description":"Ordered \"Tsuyahime\" from Great Rice. \nNeed to pick it up at 10:00 am on Sunday. \nPick up location is 1424 Howell St","createDate":"2023-05-06"}]}}
良さそうですね。実際のアプリケーションではこのエンドポイントを使用して接続すれば良さそうです。