6

I am building a chat service and I want to handle the cases when the subscription(websocket) connection is disconnected. Apollo client is configured like bellow. I removed unnecessary code like cache, authLink etc.

How do I do this with react, apollo client? If its disconnected, I would like to show that to the chat page and when the user reconnects, I would like to fetch all the missed chat messages. This is why I need to know the disconnect, connect events

Below are the relevant packages used in this app:

"@apollo/client": "^3.3.7",
"subscriptions-transport-ws": "^0.9.18",
"react": "^17.0.1"
const httpLink = new BatchHttpLink({ uri: config.API_URL })
const wsLink = new WebSocketLink({
  uri: config.WS_URL,
  options: {
    reconnect: true,
    connectionParams:{       
      authToken: accessToken,
    },
  },
})

const splitLink = split(
  ({ query }) => {
    const definition = getMainDefinition(query) 
    return definition.kind === 'OperationDefinition' && definition.operation === 'subscription'
  },
  wsLink,
  httpLink
)
const client = new ApolloClient({
  cache,
  link: from([new SentryLink(), authLink, errorLink, splitLink]),
})
Sihoon Kim
  • 1,481
  • 2
  • 13
  • 32

2 Answers2

9

I finally found the solution. It turns out that you can declare a SubscriptionClient first and then insert this into WebSocketLink, rather than declare with WebSocketLink directly.

With SubscriptionClient, you can listen to the necessary events, whereas with WebsocketLink its not really possible(or very limited).

Unfortunately, no where in Apollo docs, mentions SubscriptionClient or ways of handling connection issues.

import { WebSocketLink } from '@apollo/client/link/ws'
import { SubscriptionClient } from 'subscriptions-transport-ws' // <- import this

const wsClient = new SubscriptionClient(config.WS_URL, {
  reconnect: true,
  connectionParams: {
    authToken: accessToken,
  },
})

const wsLink = new WebSocketLink(wsClient)

By doing so, now you can listen to connection events with wsClient

wsClient.onConnected(() => console.log("websocket connected!!"))
wsClient.onDisconnected(() => console.log("websocket disconnected!!"))
wsClient.onReconnected(() => console.log("websocket reconnected!!"))

There are more events you can listen to. But these events are sufficient to implement fetching missed chat messages.

With these events, after initial connection, you can store the disconnectTimestamp on disconnect event. When you receive onReconnected event, you can simply fetch chat messages that were created after the disconnectTimestamp

Sihoon Kim
  • 1,481
  • 2
  • 13
  • 32
  • Note that `subscriptions-transport-ws` is the older, unmaintained library used for Apollo Client subscriptions: https://www.apollographql.com/docs/react/data/subscriptions/#choosing-a-subscription-library – Trevor Robinson May 17 '22 at 21:30
1

It appears that the option you'll want to use to target the WS connect/disconnect event is connectionCallback (see the full list of WebSocketLink options here).

Take a look at lines 620-635 of the WebSocketLink source and you can see that the provided connectionCallback is called both for GQL_CONNECTION_ERROR and GQL_CONNECTION_ACK received message types. Therefore, you should be able to target both events using this callback.

I haven't used Apollo's WebSocketLink yet, So I am unable to confirm that this will work fully as expected. Additionally, the behavior to fetch all missing chat messages upon reconnect is something you may need to build yourself as it doesn't appear to be part of the default reconnect behavior (will depend on server implementation; see Apollo Server docs). Conversely, it does appear that WebSocketLink will forward all unsent messages to the server upon reconnect by default.

Sam Joshua
  • 310
  • 6
  • 17
Dennis G.
  • 283
  • 1
  • 11
  • 1
    I tried connectionCallback and it does run when it establishes new connection, but it does not run when the websocket is disconnected. This is definitely an improvement, but ideally I would also like to know when it got disconencted, so that I can only fetch chat messages that occured during that period – Sihoon Kim Jun 13 '21 at 04:59