GraphQLサブスクリプションをステップごとに解説
GraphQLサブスクリプション
GraphQLサブスクリプション:通信レベルでの仕組み | We.Inc
GraphQLサブスクリプションとは何か、ルートフィールド1つのルール、graphql-wsのメッセージの流れ、SSEという選択肢、ポーリングのほうが適している場面を解説します。
GraphQLサブスクリプションは長時間続く操作です。クライアントがサブスクリプションのドキュメントを一度送ると、該当するイベントが発生するたびにサーバーが新しい結果をプッシュします。GraphQLはトランスポートを規定していません。実際にはgraphql-wsプロトコルを使ったWebSocket上か、Server-Sent Events上で動作します。各サブスクリプション操作では、ルートフィールドをちょうど1つだけ選択する必要があります。
サブスクリプション、ポーリング、再取得の比較
- サブスクリプション
- イベントごとにサーバーが結果をプッシュ
- ユーザーがすぐ見たい、小さく頻繁な変更
- ポーリング
- クライアントがタイマーでクエリを繰り返す
- ときどき変わるデータ
- 操作後の再取得
- ユーザーの操作後にクライアントが再度クエリ
- ユーザーが操作したときだけ変わる画面
- プッシュ通知
- アプリ外のメッセージ
- アプリを閉じているモバイルユーザー
下のステッパーは、graphql-wsプロトコル上の1つのサブスクリプションをメッセージごとに再生します。最初のハンドシェイクから最後のcompleteまで、各ステップでソケットを流れるJSONフレームと、それを送る側を表示します。
- 「次へ」を押して、やり取りを1フレームずつ進めます。
- 各フレームを誰が送り、何を意味するかを読みます。
- 操作のidに注目します。1つのサブスクリプションのフレームはすべて同じidを持ちます。
- 「戻る」や「最初から」で、もう一度ステップを確認できます。
- まずはポーリングから始め、数秒の遅れが本当に体験を損なう場合にだけサブスクリプションに切り替えましょう。
- 各結果には一覧全体ではなく、新しいメッセージなどの小さなイベントを入れて送り、クライアント側でマージさせましょう。
- 再接続に備えましょう。ソケットが切れた後、クライアントは再度サブスクライブし、見逃したデータを再取得する必要があります。
- 認証は接続を開いたときと、各リゾルバーの中の両方で確認しましょう。長いセッション中にユーザーの権限が変わることがあるからです。
よくある質問
なぜサブスクリプションごとにルートフィールドは1つだけなのですか?
各サブスクリプションは1つのイベントストリームに対応するからです。graphql.orgでは、1つのサブスクリプション操作に2つのルートフィールドを入れると無効になると示されています。代わりに名前付きのサブスクリプションを2つ書きましょう。
graphql-wsはどのWebSocketサブプロトコルを使いますか?
ハンドシェイク時にgraphql-transport-wsをネゴシエートし、connection_init、subscribe、next、completeなどのフレームタイプを使います。他のGraphQL WebSocketライブラリは別のプロトコルを使う場合があるため、ほかのデバッグを始める前に、クライアントとサーバーのプロトコルが一致しているか確認しましょう。
WebSocketなしでサブスクリプションを使えますか?
使えます。graphql-sseプロトコルは、各結果をnextという名前のServer-Sent Eventとして送り、completeで終了します。通常のHTTP上で動くため、WebSocketのアップグレードをブロックするプロキシの背後でも役立ちます。
サブスクリプションはクエリと同じようにスケールしますか?
しません。graphql.orgが指摘するとおり、購読中の各クライアントは1つのサーバーインスタンスに結び付き、接続中ずっと状態を保持する必要があります。そのため、サーバー間でイベントを配信するpub/subシステムも必要になります。
サブスクライブすると何が起こるか
- クライアントは、subscriptionキーワードで始まり、ルートフィールドを1つ選び、各イベントで欲しいフィールドを列挙したドキュメントを送ります。サーバーはそれを一度検証し、開いたままにします。
- 新しいレビューなど該当するイベントが発行されると、サーバーはそのイベントに対して選択セットを実行し、結果を送ります。各結果は通常のdataとerrorsの形をしているため、クライアントのコードはクエリと同じように読み取れます。
graphql-wsプロトコルの概要
- WebSocketのアップグレード後、クライアントはconnection_initを送ります。ペイロードには認証トークンを入れることが多く、その後connection_ackを待ちます。subscribeフレームを送れるのはその後だけです。各subscribeは一意のidを持ち、その操作に関するnext、error、completeの各フレームも同じidを繰り返すため、1つのソケットで多数のサブスクリプションを共有できます。
- どちらの側もpingを送ることができ、相手はpongで応答します。connection_initが時間内に届かない場合、サーバーはコード4408でソケットを閉じます。まだ実行中のidをクライアントが再利用した場合は4409で閉じます。
ポーリングのほうが適している場合
- サブスクリプションには、スティッキーな接続、pub/subのレイヤー、再購読や競合状態に対応する慎重なクライアントコードが必要です。1分ごとに更新するダッシュボードなら、タイマーでクエリするほうがシンプルで、障害時の振る舞いも穏やかです。
- graphql.orgは、頻繁に少しずつ変わるデータにはサブスクリプションを、変化の遅いデータや低帯域のモバイルクライアントにはポーリング、プッシュ通知、再取得を勧めています。
スキーマとクライアントのコード
サブスクリプションフィールドを宣言する
SubscriptionはQueryやMutationと同じルート型です。各フィールドが、クライアントが開ける1つのストリームになります。
graphql-wsクライアントでサブスクライブする
createClientはソケットを必要になったときに開き、iterateはイベントごとに1つの結果を返す非同期イテレーターを返します。
ルートフィールド1つのルール
2つのストリームを2つの操作に分ける
reviewCreatedと別のルートフィールドを並べた1つのサブスクリプションは検証に失敗します。名前付きの操作を2つにすれば動作します。
クライアントがconnection_initを送信
ソケットが開いた直後に送られます。ペイロードの形式は自由で、サーバーはそこからトークンを読み取ることがよくあります。
サーバーがconnection_ackを送信
サーバーが接続を受け入れました。これより前にsubscribeフレームを送ると、ソケットは4401 Unauthorizedで閉じられます。
クライアントがsubscribeを送信
idはクライアントが決め、実行中の操作の中で一意でなければなりません。ペイロードは通常のクエリと変数です。
サーバーがnextを送信
レビューが投稿されたので、サーバーは選択セットを実行し、同じidで結果を送りました。
サーバーが再びnextを送信
イベントがあるたびに新しいフレームが届きます。サブスクリプションが開いている限り、これが繰り返されます。
クライアントがcompleteを送信
ユーザーがページを離れたので、クライアントは操作を停止します。ソケットは他のサブスクリプションのために開いたままにできます。
1つのサブスクリプションをフレームごとに
戻る
次へ
最初から
ステップ {n} / {v}
ソケット上のフレーム
フレームをコピー
すべてのステップ
無料で始める · クレジットカード不要
関連: ガイド|Webマーケティングと集客を学ぶ
製品
ご利用いただける方
機能
リソース
会社情報
サイトマップを見る