LoginSignup
5
1

More than 1 year has passed since last update.

URQLでSubscriptionを使おうとしたらWebSocketのプロトコルが違った話

Last updated at Posted at 2022-06-21

はじめに

URQLでGraphQLのSubscriptionを使用しようとした際に結果が表示されないというエラーが発生し、長い間悩まされたので同じ被害者が出ないように残しておこうと思います。

TL;DR

  • サーバー(NestJS), クライアント(Next.js)をgraphql-wsで統一しても動作しない
  • subscriptions-transport-wsでは正常に動作する

環境

ツール・ライブラリ 用途 バージョン
Next.js フロントエンドとして利用 12.1.6
NestJS サーバーサイドとして利用 8.4.6
GraphQL Code Generator コード自動生成ツール 2.6.2
URQL GraphQL Clientのライブラリ 2.2.1
node.js フロント、サーバーサイドの実行環境 v16.14.2

課題

課題内容は冒頭でも簡単に書いたのでこのエラーに嵌った経緯を書いていきます。

まずGraphQLのSubscriptionを使用する際はリクエストを送信するためのプロトコルは定義されていないので、WebSocket上でサブスクリプションを実装した以下のライブラリのどちらかを使う必要があります。

  • graphql-ws
  • subscriptions-transport-ws

subscriptions-transport-wsは活発にメンテナンスが行われておらず、公式ではその後継にあたるgraphql-wsを使用することを推奨していました。

なので公式に倣ってgraphql-wsを利用しました。

しかし、結果は以下のようにdataも表示されなければerrorも表示されませんでした。

result:
  data: undefined
  error: undefined
  extensions: undefined
  fetching: false
  operation: undefined
  stale: false

ちなみにサーバー側はNestJSで実装し、graphql-ws, subscriptions-transport-wsの両方をonにしました。
※公式でも両方利用することを許容していました。

解決方法

URQLクライアントのSubscription WSをgraphql-wsからsubscriptions-transport-wsに変更したところ動作しました。

urql-client.ts
import {
  createClient,
  dedupExchange,
  Exchange,
  fetchExchange,
  Provider,
} from 'urql'
import { authConfig } from '@/utils/urql/urql-auth-config'
import { graphCacheConfig } from '@/utils/urql/urql-graphcache-config'
import { subscriptionCustomExchange } from '@/utils/urql/urql-subscription-config'

export const urqlExchanges = [
  dedupExchange,
  cacheExchange(graphCacheConfig),
  authExchange(authConfig),
  fetchExchange,
  subscriptionCustomExchange,
].filter(Boolean) as Exchange[]

const createUrqlClient = () =>
  createClient({
    url: 'http://localhost:8000/graphql',
    suspense: true,
    exchanges: urqlExchanges,
  })
urql-subscription-config.ts
import { SubscriptionClient } from 'subscriptions-transport-ws'
import { Exchange, subscriptionExchange } from 'urql'

const isSSR = typeof window === 'undefined'

console.log({ isSSR })

const transportWsClient = !isSSR
  ? new SubscriptionClient('ws://localhost:8000/graphql', { reconnect: true })
  : undefined

export const subscriptionCustomExchange: Exchange | null = transportWsClient
  ? subscriptionExchange({
      forwardSubscription: operation => {
        return transportWsClient.request(operation)
      },
    })
  : null

余談

こちらにも書いてあるように、graphql-wsライブラリのサブプロトコルをgraphql-transport-wsと呼び、subscriptions-transport-wsライブラリのサブプロトコルをgraphql-wsと呼んでいます。

なんともまぁややこしいです笑

今回のケースで言うと、プロトコルをgraphql-transport-wsからgraphql-wsに変えたらうまく動作することができました。

公式にも書いてあるとおり活発なメンテナンスが行われていないため非推奨になっているのでgraphql-wsで使えるようになるのはいつなのかは引き続き調査しようと思います。

ではまた!

参考記事

5
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
1