メインコンテンツにスキップ
バージョン: v18.0.0

GraphQL サブスクリプション

GraphQL サブスクリプションは、サーバーサイドイベントのストリームに応じてクライアントがデータをクエリできるようにするメカニズムです。

GraphQL サブスクリプションは、`subscription` キーワードを使用する点を除けば、クエリと非常によく似ています。

subscription FeedbackLikeSubscription($input: FeedbackLikeSubscribeData!) {
feedback_like_subscribe(data: $input) {
feedback {
like_count
}
}
}
  • この GraphQL スニペットを使用してサブスクリプションを確立すると、`feedback_like_subscribe` ストリームからイベントが発行されるたびにアプリケーションに通知されます。
  • `feedback_like_subscribe` は、バックエンドでサブスクリプションを設定する *サブスクリプションルートフィールド* (または単に *サブスクリプションフィールド*) です。
  • ミューテーションと同様に、サブスクリプションは 2 つのステップで処理されます。最初に、サーバーサイドイベントが発生します。次に、クエリが実行されます。
注記

イベントストリームは完全に任意であり、選択されたフィールドとは無関係であることに注意してください。つまり、サブスクリプションで選択された値が通知ごとに変更されるという保証はありません。

  • `feedback_like_subscribe` は、サーバーサイドイベントに応じてクエリできるデータを公開する特定の GraphQL 型を返します。この場合、Feedback オブジェクトとその更新された `like_count` をクエリしています。これにより、いいね数をリアルタイムで表示できます。

クライアントが受信したサブスクリプションペイロードの例は次のようになります。

{
"feedback_like_subscribe": {
"feedback": {
"id": "feedback-id",
"like_count": 321,
}
}
}

Relay では、`graphql` タグを使用して GraphQL サブスクリプションを宣言することもできます。

const {graphql} = require('react-relay');

const feedbackLikeSubscription = graphql`
subscription FeedbackLikeSubscription($input: FeedbackLikeSubscribeData!) {
feedback_like_subscribe(data: $input) {
feedback {
like_count
}
}
}
`;
  • サブスクリプションは、クエリやフラグメントと同じ方法で GraphQL 変数 を参照できることに注意してください。

`useSubscription` を使用してサブスクリプションを作成する

Relay でサブスクリプションを作成するには、`useSubscription` API と `requestSubscription` API を使用できます。`useSubscription` API を使用した例を見てみましょう。

import type {Environment} from 'react-relay';
import type {FeedbackLikeSubscribeData} from 'FeedbackLikeSubscription.graphql';

const {graphql, useSubscription} = require('react-relay');
const {useMemo} = require('React');

function useFeedbackSubscription(
input: FeedbackLikeSubscribeData,
) {
const config = useMemo(() => ({
subscription: graphql`
subscription FeedbackLikeSubscription(
$input: FeedbackLikeSubscribeData!
) {
feedback_like_subscribe(data: $input) {
feedback {
like_count
}
}
}
`,
variables: {input},
}), [input]);

return useSubscription(config);
}

ここで何が起こっているのかを詳しく見てみましょう。

  • `useSubscription` は、次のフィールドを含む `GraphQLSubscriptionConfig` オブジェクトを受け取ります。
    • `subscription`: サブスクリプションを含む GraphQL リテラル、および
    • `variables`: サブスクリプションを確立するための変数。
  • さらに、`useSubscription` は Flow 型パラメータを受け入れます。クエリと同様に、サブスクリプションの Flow 型は、Relay コンパイラが生成するファイルからエクスポートされます。
    • この型が提供されている場合、`GraphQLSubscriptionConfig` も静的に型付けされます。 **常にこの型を提供することをお勧めします。**
  • `useFeedbackSubscription` フックがコミットされると、Relay はサブスクリプションを確立します。
    • `useLazyLoadQuery` のような API とは異なり、Relay はレンダリングフェーズ中にこのサブスクリプションを確立しようとは **しません**。
  • 確立されると、イベントが発生するたびに、バックエンドは更新された Feedback オブジェクトを選択し、そこから `like_count` フィールドを選択します。
    • `Feedback` 型には `id` フィールドが含まれているため、Relay コンパイラは `id` フィールドの選択を自動的に追加します。
  • サブスクリプションレスポンスを受信すると、Relay はストア内で一致する `id` を持つフィードバックオブジェクトを見つけ、新しく受信した `like_count` 値で更新します。
  • これらの値が変更された結果、フィードバックオブジェクトからこれらのフィールドを選択したコンポーネントは再レンダリングされます。つまり、更新されたデータに依存するコンポーネントは再レンダリングされます。
注記

パラメータ `FeedbackLikeSubscribeData` の型の名前は、トップレベルのミューテーションフィールドの名前、つまり `feedback_like_subscribe` から派生しています。この型は、生成された `graphql.js` ファイルからもエクスポートされます。

注意

`useSubscription` に渡される `GraphQLSubscriptionConfig` オブジェクトはメモ化される必要があります!そうでない場合、`useSubscription` はサブスクリプションを破棄し、レンダリングごとに再確立します!

サブスクリプションイベントに応答してコンポーネントを更新する

前の例では、手動で `like_count` を選択しました。このフィールドを選択するコンポーネントは、更新された値を受信した場合に再レンダリングされます。

ただし、一般的には、ミューテーションに応じて更新するコンポーネントに対応するフラグメントをスプレッドする方が良いでしょう。これは、コンポーネントによって選択されたデータが変更される可能性があるためです。

開発者に、コンポーネントのデータを取得する可能性のあるすべてのサブスクリプションについて知ってもらうこと (そしてそれらを最新の状態に保つこと) を要求することは、Relay が避けたいと考えているグローバルな推論の一例です。

たとえば、次のようにサブスクリプションを書き直すことができます。

subscription FeedbackLikeSubscription($input: FeedbackLikeSubscribeData!) {
feedback_like_subscribe(data: $input) {
feedback {
...FeedbackDisplay_feedback
...FeedbackDetail_feedback
}
}
}

これで、`feedback_like_subscribe` イベントストリームでイベントが発生するたびに、`FeedbackDisplay` コンポーネントと `FeedbackDetail` コンポーネントによって選択されたデータが再フェッチされ、それらのコンポーネントは一貫した状態を維持します。

注記

更新されたデータは 1 回のラウンドトリップでフェッチできるため、フラグメントをスプレッドすることは、サブスクリプションイベントに応じてデータを再フェッチするよりも一般的に望ましいです。

サブスクリプションが発生、エラーが発生、またはサーバーによって閉じられたときにコールバックを実行する

更新されたデータを Relay ストアに書き込むことに加えて、サブスクリプションペイロードが受信されるたびにコールバックを実行したい場合があります。エラーを受信した場合、またはサーバーがサブスクリプションを終了した場合にコールバックを実行したい場合があります。`GraphQLSubscriptionConfig` には、このような場合を処理するための次のフィールドを含めることができます。

  • `onNext`: サブスクリプションペイロードが受信されたときに実行されるコールバック。サブスクリプションレスポンスが渡されます (フラグメントスプレッド境界で停止)。
  • `onError`: サブスクリプションエラーが発生したときに実行されるコールバック。発生したエラーが渡されます。
  • `onCompleted`: サーバーがサブスクリプションを終了したときに実行されるコールバック。

宣言的なミューテーションディレクティブ

宣言的なミューテーションディレクティブ`@deleteRecord` は、サブスクリプションでも機能します。

サブスクリプションイベントに応答してコネクションを操作する

Relay を使用すると、コネクション (つまりリスト) にアイテムを追加または削除することで、サブスクリプションイベントに簡単に対応できます。たとえば、新しく作成されたユーザーを特定のコネクションに追加する場合があります。詳細については、宣言型ディレクティブの使用 を参照してください。

ミューテーションに応答してアイテムを削除する

さらに、ミューテーションに応じてストアからアイテムを削除する場合があります。これを行うには、削除された ID に `@deleteRecord` ディレクティブを追加します。例:

subscription DeletePostSubscription($input: DeletePostSubscribeData!) {
delete_post_subscribe(data: $input) {
deleted_post {
id @deleteRecord
}
}
}

ローカルデータを命令的に変更する

場合によっては、実行したい更新がフィールドの値を更新するよりも複雑で、宣言的なミューテーションディレクティブでは処理できないことがあります。このような状況では、`GraphQLSubscriptionConfig` は、ストアの更新方法を完全に制御できる `updater` 関数を受け入れます。

これについては、ストアデータを命令的に更新する のセクションで詳しく説明しています。

ネットワーク層の構成

サブスクリプションを処理するには、ネットワーク層 を構成する必要があります。

通常、GraphQL サブスクリプションは WebSocket 経由で通信されます。 graphql-ws を使用した例を次に示します。

import {
...
Network,
Observable
} from 'relay-runtime';
import { createClient } from 'graphql-ws';

const wsClient = createClient({
url:'ws://localhost:3000',
});

const subscribe = (operation, variables) => {
return Observable.create((sink) => {
return wsClient.subscribe(
{
operationName: operation.name,
query: operation.text,
variables,
},
sink,
);
});
}

const network = Network.create(fetchQuery, subscribe);

または、レガシーの subscriptions-transport-ws ライブラリも使用できます。

import {
...
Network,
Observable
} from 'relay-runtime';
import { SubscriptionClient } from 'subscriptions-transport-ws';

const subscriptionClient = new SubscriptionClient('ws://localhost:3000', {
reconnect: true,
});

const subscribe = (request, variables) => {
const subscribeObservable = subscriptionClient.request({
query: request.text,
operationName: request.name,
variables,
});
// Important: Convert subscriptions-transport-ws observable type to Relay's
return Observable.from(subscribeObservable);
};

const network = Network.create(fetchQuery, subscribe);

このページは役に立ちましたか?

簡単な質問に答えることで、サイトの改善にご協力ください。 いくつかの簡単な質問に答える.