こんにちは、クロスパワーの佐々木です。
前編を書いてからそろそろ100日目を迎えようとしており、この間言われるまで書いたことすら忘れておりました…
内容まで忘れてしまう前に完結させてしまいましょう!
さて、AWS AppSyncについて知ったかぶりをするには、前編だけでは少し足りません。
それでは、自信を持って "知ったかぶり" していただくために、(前回の内容から漏れていた)
「Subscription」と「リゾルバ」
について触れていきたいと思います。
※前編…【AWS AppSyncでGraphQLへの入門が「easy」になる(前編)】
前編のおさらい
前編ではざっくりと、下記について説明いたしました。
AWS AppSync構成
項目 | 詳細 |
---|---|
クエリ | スキーマに定義されたメソッドを用いて、リクエストを行う。 ※戻り値はスキーマに定義している範囲で制御可能 |
スキーマ | アプリケーションでクエリに使用できるメソッドの定義。 ※件数指定、フィルターもここで定義する |
リゾルバ | 各パラメータを受け取って、リソースに行う処理を定義。 ※リソースには、Lambdaを指定することも可能 |
前編の内容のレベル感をざっくりまとめますと、
「クエリについてが中心なので、既存のGraphQL APIを利用するだけならできなくもない」
といったところでしょうか。
Subscription~概要~
アプリケーションで、DBのデータと同期をとるときに皆さんならどういった方法を使いますか?
"ポーリング″ ? "ロングポーリング″ ? "SSE″ ? "WebSocket″?
※主題とずれるので、詳しくはこちら→https://qiita.com/yuba/items/00fc1892b296fb7b8de9
GraphQLはその方法の一つとして、「Subscription」なるものを用意しています。
Pub/Sub(PublisherとSubscriberモデル)とも言います。
※直訳すると、「出版社と加入者」ですね。
「定期購読したい人の元に、記事が出来上がったら出版社が配達してくれる」
というイメージは確かにこのモデルを上手く表現していると思います。
仕組みとしては、下記の通りです。
アプリ側
Subscriptionのクエリを実行して、更新データが来るのを待機。
APIサーバー側
DBのテーブルを監視するのではなく、mutation処理の発生時にトリガーされ、アプリケーションに更新データを送信します。
→掲示板や、SNSアプリなどリアルタイム性が要求されるアプリでは、重宝したいところですね。
Subscription~実践~
それではさっそく、Subscriptionを使ってみましょう!
手順に沿って進めてみてください。
【注意!】前編でイベントアプリを作成したことが前提となっております。
手順
- スキーマでSubscriptionを定義
- AWS AppSyncのコンソール上でSubscriptionのクエリを実行
- PostmanでcreateEventのクエリを実行
- AWS AppSyncのコンソール上で、変更データを受け取れていることを確認
1. スキーマでSubscriptionを定義
スキーマ】
onCreateEvent(id: Int): Event @aws_subscribe(mutations: ["createEvent"])
どのイベントを元にデータをパブリッシュするか、@aws_subscribeディレクティブで定義しました。
※この配列には複数のイベント名を入力することが可能です。(下記、例になります)
@aws_subscribe(mutations: ["createEvent", "updateEvent"])
【クエリ】
subscription MySubscription { onCreateEvent { id name description } }
※引数として、IDを指定すればそのIDのイベントのみサブスクライブして、他のイベントが生成されても無視します。
IDだと少し使い道が見えないかもしれませんが、こちらの記事をみると用途に納得できると思います。
https://qiita.com/ronny/items/7a21cce4e607b0d6d632
2. AWS AppSyncのコンソール上でSubscriptionのクエリを実行
1でスキーマを定義して、クエリを書いたあとは実行ボタンを押すだけです。
実行すると下記のようにローディングマークが出現して、他のクエリが同画面からだと実行できなくなります。
※もしエラーが発生した方は、後述の「エラー」を参照してみてください。
3. PostmanでcreateEventのクエリを実行
mutationのイベントをPostmanのクエリに書きます。
今回は下記のようなクエリを書きました。
mutation MyMutation { createEvent(name: "隅田川花火大会", description: "浅草は隅田川にて催される日本最古の花火大会", when: "2020/07/11", where: "Asakusa") { id name description } }
結果は以下の通り、イベントを作成したと同時に
待機していたAWS AppSyncのコンソール画面で、作成されたイベントが届きました。
(何回実行しても、問題なくサブスクライブするはずです)
【Postman】
【AWS AppSyncコンソール】
以上でSubscriptionが完了。
エラー
- 現象 -
【スキーマ】
手順に記載の通り。
【クエリ】
subscription MySubscription { onCreateEvent { id name description } } mutation MyMutation { createEvent(description: "詳細", name: "佐々木", when: "2020/09/25", where: "Las Vegas") { description id name } }
これでクエリを実行したところ下記のエラーが発生。
Error: { "errors": [ { "message": "Connection failed: {\"errors\":[{\"errorType\":\"UnsupportedOperation\",\"message\":\" not supported through the realtime channel\"}]}" }
- 解決方法 -
実に簡単に解決できました。
クエリに記載されているSubscription以外の全てを削除します。
※【注意】スキーマではありません!
参考:https://github.com/awslabs/aws-mobile-appsync-sdk-ios/issues/372
リゾルバーを読み解く
今はリゾルバーをどう書いているのかというと、デフォルトのテンプレートを流用しているだけになります。
表示方法
実際にリゾルバーを編集する画面を開くには、まずはスキーマを開いて
「各Fieldに割り当てられたリゾルバー」または「未割当の場合にはアタッチ」をクリックします。
その後、「Configure mapping templates」にてテンプレートを選択、必要に応じて編集します。
これにて表示は完了です。
テンプレートを読み解く
さて、さっそく中身に触れていきましょう。
テンプレートには下記のようなものがあります。
Get item by id / List items / Put items / Delete item by id / Query with filter on index / Simple query with pagination / etc.
今回はこの中から抜粋して、下記の2つについて読み解いていこうと思います。
ごく簡単な説明ではございますが、お付き合い願います。
1. Put items
2. Query with filter on index
1. Put items
【テンプレート】
{ "version" : "2017-02-28", "operation" : "PutItem", "key": { "id" : $util.dynamodb.toDynamoDBJson($util.autoId()) }, "attributeValues" : $util.dynamodb.toMapValuesJson($ctx.args) }
この書き方で気になる点と言えば、下記の2点だと思います。
$util.dynamodb.toDynamoDBJson $util.dynamodb.toMapValuesJson
これらは楽をせずに全てを書きますと、下記のようになります。
{ "version" : "2017-02-28", "operation" : "PutItem", "key": { "id" : { "S" : "$util.autoId()" } }, "attributeValues" : { "title" : { "S" : $util.toJson($ctx.args.title) }, "author" : { "S" : $util.toJson($ctx.args.author) }, "version" : { "N", $util.toJson($ctx.args.version) } } }
それぞれの値に対して、{型:値}として渡す必要があるわけです。
その上、attributeValuesでは、引数から値を取り出してDynamoのカラム名に手動でマッピングしていく必要があります。
※型の書き方が若干特殊ですが、「S → String」,「N → Number」を意味すると思われます。
しかし、先ほどのDynamoDBヘルパーを用いれば、
引数をまとめて受け取って、DynamoDB 入力形式に変換してくれます。
※「toDynamoDBJson」は、 String型のDynamoDB入力形式に変換してくれます。
2. Query with filter on index
{ "version" : "2017-02-28", "operation" : "Query", "index" : "name-index", "query" : { "expression": "#name = :name", "expressionNames" : { "#name" : "name" }, "expressionValues" : { ":name" : $util.dynamodb.toDynamoDBJson($ctx.args.name) } }, "filter" : { "expression" : "contains(#city, :city)", "expressionNames" : { "#city" : "city" }, "expressionValues" : { ":city" : $util.dynamodb.toDynamoDBJson($ctx.args.city) } } }
ここのqueryとfilterはそれぞれ、下記の通りです。(その他のプロパティについては参照をご覧ください)
Query...DynamoDB から取得する項目を指示するキー条件式を指定することができます。(DBへのクエリリクエスト)
Filter...取得してきたデータを条件式を元にフィルタリングします。
expression | クエリやフィルターの式 |
expressionNames | expressionで使われるDynamoDBのデータを、カラム名で変数に紐づける。 テンプレートだと、カラム名「city」のデータを「#city」という変数に紐づけている。 |
expressionValues | expressionで使われる引数値を、新たな変数名に紐づける。※型が必須 テンプレートだと、「引数のcity」をString型で「:city」という変数に紐づけている。 |
また、expressionで使われる演算子(containsや=など)は、
参照1に記載のものが使用可能です。("begins_with"や"beween"など)
参照1:
https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/LegacyConditionalParameters.KeyConditions.html
まとめ
前回に続き、今回はSubscription機能をメインに試してみました。加えて、リゾルバのテンプレートを通じて、基本的な書き方についてみてみました。
これで皆さんも立派なGraphQL使いですね!
機会があれば是非「オフラインからの同期機能」も試してみたいところです!
(オフラインでクエリを実行し、オンラインになったら自動的に競合解決することが可能なようです。→https://aws.amazon.com/jp/appsync/product-details/#Offline_data_synchronization)
おまけ
余談ですが、久々にAppSyncコンソールを開いたらクエリの画面が真っ白になっていました。
そんな時には、設定>デフォルトの認証モード からAPIキーを「New」しましょう。すぐにまた使えるようになります。