GraphQLにおけるSubscription処理について(実装例: Amplify + AppSync)
はじめに
- こんにちは ! KDDI アジャイル開発センターの小板橋です。
- この記事は、KDDI Engineer Advent Calendar 2020の15日目の記事となります。
- GraphQLの記事はよく見かけますが、GraphQLにおけるSubscription処理についてはあまり目にすることがありません。そこで本記事は、Subscriptionに着目したもので、実装時の助けになれれば幸いです。
- GraphQLについては、以前事細かく書いたので、そちらをご覧ください。GraphQL入門
GraphQLにおけるSubscription処理
仕組み
realtime API
pub/subのような仕組みのAPIをrealtime APIというらしいです。
参考: GraphQL Subscription
このrealtime APIなのですが、大きく分けて3つのタイプに分類されます。
1.ポーリング
クライアントは、定期的にリクエストを発行し、関心のあるデータの状態を確認します。
ポーリングの特徴としては、調整が困難なことです。というのも、更新の頻度が低い場合は、ポーリングは無駄になります。逆に、更新が頻繁に行われると、ポーリングによる待ち時間が長くなります。
2.イベントベースのサブスクリプション
クライアントは、1つ以上のイベントを処理することをサーバーに通知します。これらのイベントがトリガーされるたびに、サーバーはクライアントに通知します。
このモデルでは、サーバーがイベントを識別し、それらを事前に通知する方法を特定する必要があります。
3.ライブクエリ
クライアントは、クエリを発行します。クエリへの返答が変わるたびに、サーバーは新しいデータをクライアントにプッシュします。
ライブクエリとイベントベースのサブスクリプションの主な違いは、ライブクエリはイベントの概念に依存しないことです。
データ自体はライブであり、変更を通知する方法が含まれています。
ではGraphQLのSubscriptionは??
GraphQLのSubscriptionを使用すると、クライアントはサーバーにGraphQLのクエリとクエリ変数を送信します。
サーバーは、これらの入力を特定のイベント処理にマップし、イベントがトリガーされたときにクエリを実行します。
Pub/Subの仕組み(ライフサイクル)
Pub/Subのライフサイクルなのですが、次のようになります。
-
Subscribeの設定
→ クライアントは、クエリとそのクエリ変数をサーバーに送信することにより、Subscriptionを初期化します。Subscriptionが作成されると、クエリとクエリ変数は、Subscriptionを設定しているイベントにマップされます。 -
Subscribeの解除
→ Subscriptionの登録を解除すると、ペイロードを受信しなくなります。
これは、クライアントが明示的にサブスクライブを解除した場合、またはサーバーがサブスクライブの解除を行う必要があると判断した場合、または、クライアントが切断された場合に発生する可能性があります。
-
Publishの実行
→ Subscriptionに関連付けられたイベントがトリガーされると、Subscriptionはクエリ、クエリ変数、およびペイロードを実行し、結果をクライアントに送信します。
実装する
- 今回はAWSのAppSync + Client側はAmplifyを利用した場合の、Subscription処理を実装していきます。
AppSyncにおけるSubscription処理
AppSyncのSubscriptionは、ミューテーションに対する応答として呼び出される形になります。
その為、スキーマのミューテーションで指定することで、AppSyncで任意のデータソースをリアルタイム対応にすることができます。
また、AppSyncの便利なところは、AppSyncクライアントのSDKは、Subscriptionの接続管理を自動的に処理するところです。
ちなみに、クライアントとサービス間のネットワークプロトコルとして、WebSocketまたはMQTT over WebSocketsのいずれかを使用するようです。
スキーマの設定方法
例えば下記のようなミューテーションがあったとします。
type Post {
id: ID!
user: String!
}
input CreatePostInput {
id: ID
user: String!
}
type Mutation {
createPost(input: CreatePostInput!): Post
}
type Subscription {
CreatePostInput: Post
}
そして、通知を受け取る各サブスクリプションに @aws_subscribe(mutations: ["mutation_field_1", "mutation_field_2"]) ディレクティブを追加することで、これらのフィールドをリアルタイムに対応できます。
type Subscription {
addedPost: Post
@aws_subscribe(mutations: ["CreatePostInput"])
}
これで、AppSync側の設定は良いのですが、気をつけなければならないのは、Client側の実装です。
Client側の実装
まず重要なことは、client側のSubscriptionの定義の仕方によって欲しいデータを定義になければならない点です。上記の例ですと、下記のようになります。
subscription CreatePostInputSub {
CreatePostInput {
id
user
}
}
Amplify GraphQLクライアントの使用方法
- AppSyncのSubscriptionを使用する場合は、aws-exports.ts(js)のAppSync設定を下記のように設定します。
Amplify.configure({
Auth: {
identityPoolId: 'xxx',
region: 'xxx' ,
cookieStorage: {
domain: 'xxx',
path: 'xxx',
secure: true
}
},
aws_appsync_graphqlEndpoint: 'xxxx',
aws_appsync_region: 'xxxx',
aws_appsync_authenticationType: 'xxxx',
aws_appsync_apiKey: 'xxxx'
});
- 実際のコードとしては下記のようになります。
import Amplify, { API, graphqlOperation } from 'aws-amplify';
import * as subscriptions from './graphql/subscriptions';
const subscription = API.graphql(
graphqlOperation(subscriptions.onCreateTodo)
).subscribe({
next: ({ provider, value }) => console.log({ provider, value })
});
Subscriptionにより取得したデータを利用しclient側で処理を行いたい場合は、subscribeにより取得した引数のvalueを使い回すようにすれば良いのです。
subscribe({
next: ({ provider, value }) => console.log({ provider, value })
})
おまけ
ちなみに上の実装だと、tsを利用している場合、GraphQLにおけるSubscriptionの型で怒られます。。
それを回避する方法を下記に残しておきます。
Observableについては、また次回別記事で書いていこうと思います。
import Observable from 'zen-observable'
const subscription = API.graphql(graphqlOperation(subscriptions.onCreateTodo) as Observable<object>
Discussion