Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
8
Help us understand the problem. What are the problem?

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の登録を解除すると、ペイロードを受信しなくなります。
    これは、クライアントが明示的にサブスクライブを解除した場合、またはサーバーがサブスクライブの解除を行う必要があると判断した場合、または、クライアントが切断された場合に発生する可能性があります。
    スクリーンショット 2020-12-14 21.41.31.png

  • Publishの実行
    → Subscriptionに関連付けられたイベントがトリガーされると、Subscriptionはクエリ、クエリ変数、およびペイロードを実行し、結果をクライアントに送信します。
    スクリーンショット 2020-12-14 21.43.11.png

実装する

  • 今回はAWSのAppSync + Client側はAmplifyを利用した場合の、Subscription処理を実装していきます。

AppSyncにおけるSubscription処理

AppSyncのSubscriptionは、ミューテーションに対する応答として呼び出される形になります。
その為、スキーマのミューテーションで指定することで、AppSyncで任意のデータソースをリアルタイム対応にすることができます。
また、AppSyncの便利なところは、AppSyncクライアントのSDKは、Subscriptionの接続管理を自動的に処理するところです。
ちなみに、クライアントとサービス間のネットワークプロトコルとして、WebSocketまたはMQTT over WebSocketsのいずれかを使用するようです。

スキーマの設定方法

例えば下記のようなミューテーションがあったとします。

sample1.graphql
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"]) ディレクティブを追加することで、これらのフィールドをリアルタイムに対応できます。

sample2.graphql
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設定を下記のように設定します。
aws-exports.ts
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'
});

  • 実際のコードとしては下記のようになります。
sanple3.ts
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については、また次回別記事で書いていこうと思います。

sanple4.ts
import Observable from 'zen-observable'

const subscription = API.graphql(graphqlOperation(subscriptions.onCreateTodo) as Observable<object>

引用元

GraphQL Subscriptions
AWS公式ドキュメント
Amplifyドキュメント

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
8
Help us understand the problem. What are the problem?