Beginner's guide to using Websockets in Apollo Client GraphQL Subscription
Tags: graphql, react, litmuschaos, tutorial
Published: 2020-10-08 12:44:13

[GraphQL](https://graphql.org/) started to be a great option for writing customizable API’s and combining multiple services into one single endpoint. 

The entire idea of having a single endpoint but getting different combinations of models is amazing, especially for companies which are working with large scale multi-platform applications with different frontend/mobile developers and designers.

---

<p align="center">
<img src="https://external-content.duckduckgo.com/iu/?u=https%3A%2F%2Fmiro.medium.com%2Fmax%2F800%2F1*_6Zt1h5jopuP9syi-VDoMg.jpeg&f=1&nofb=1" width="1000">
</p>

## What is a WebSocket?

Typically, **WebSocket** is a protocol which provides a way to exchange data between browsers and servers via a persistent connection.

It's very useful for applications that require continuous data exchange, eg- online games, real-time systems, trading engines, and so on.

The way we open a new WebSocket connection is by using a special `ws` protocol in our regular URL. For example:-

**Regular URL:** `http://someurl.com`
**WebScoket URL:** `ws://someurl.com`

---

<p align="center">
<img src="https://external-content.duckduckgo.com/iu/?u=https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1200%2F1*0w3tMXm7jr174bqOprcdOg.png&f=1&nofb=1" width="1000">
</p>

## What about `wss`?

Just like an HTTPS is more secure than any HTTP request! `wss` is an encrypted protocol just like HTTPS but for WebSockets.

**wss** is not only encrypted but also much more reliable. That’s because `ws://` data is not encrypted, visible for any intermediary. Old proxy servers do not know about WebSocket, they may see “strange” headers and abort the connection.

On the other hand, `wss://` is WebSocket over **TLS**, (same as HTTPS is HTTP over TLS), the transport security layer encrypts the data at sender and decrypts at the receiver. So data packets are passed encrypted through proxies. They can’t see what’s inside and let them through.

---

## What is a GraphQL Subscription!

Like queries, Subscriptions are a way to fetch data. Unlike queries, subscriptions maintain an active connection to your GraphQL server via WebSocket. This enables your server to push updates to the subscription's result over time.

They are very useful to notify the client about any updates to the real-time requests. If the back-end data changes at any point, the client will immediately get notified through the already active server connection.

<p align="center">
<img src="https://external-content.duckduckgo.com/iu/?u=https%3A%2F%2Fjivimberg.io%2Fimages%2Fposts%2F2018-10-24%2FSubscriptionFlow.png&f=1&nofb=1" width="1000">
</p>


---

<p align="center">
<img src="https://external-content.duckduckgo.com/iu/?u=https%3A%2F%2Fwww.asapdevelopers.com%2Fwp-content%2Fuploads%2F2018%2F07%2FApolloGraphQL-1024x342.png&f=1&nofb=1" width="1000">
</p>

## Where does Apollo Client come into the picture

The **Apollo Client** is a comprehensive state management library for JavaScript that enables you to manage both local and remote data with GraphQL. It can be used to fetch, cache, and modify application data, all while automatically updating your UI.

The core `@apollo/client` library provides built-in integration with React, and the larger Apollo community maintains integrations for other popular view layers. Thus it comes in handy for developers looking to manage GraphQL data in the Front-End.

### Defining a Subscriber

A Subscriber has to be defined both on the server as well as the client-side. However, in this blog, we are specifically going to talk about handling subscriber in the client-side since server-side subscriber configuration could vary based on your project requirements. 

You could set up a subscriber server using goLang, Node JS, etc but the client-side handling would pretty much remain the same for different tools, libraries and frameworks.

---

In this blog, I'm going to discuss how to handle client-side subscription with Apollo using reference from an Open Source Project called [LitmusChaos](https://github.com/litmuschaos/litmus).

![Litmus](https://i.ibb.co/R4xR644/Image-from-i-OS.jpg)

[**Litmus**](https://github.com/litmuschaos/litmus) is a toolset to do cloud-native chaos engineering. Litmus provides tools to orchestrate chaos on Kubernetes to help developers and SREs find weaknesses in their application deployments. Litmus can be used to run chaos experiments initially in the staging environment and eventually in production to find bugs, vulnerabilities.

The project is under active development as a [Sandbox project with CNCF](https://www.cncf.io/sandbox-projects/).

## Our Subscriber Definition

Since [Litmus](https://github.com/litmuschaos/litmus) is a project where we deal with a lot of Chaos Experiments and Workflow Data, we have set up our Subscriber such that we listen to the Workflow Events and retrieve a number of essential parameters which are required to plot Analytics Graph, Workflow Representation Data and so on.

```bash
const WORKFLOW_EVENTS = gql`
  subscription workflowEvents($projectID: String!) {
    workflowEventListener(project_id: $projectID) {
      workflow_id
      workflow_name
      workflow_run_id
      execution_data
      project_id
      cluster_name
      last_updated
    }
  }
`;
```

When Apollo Client executes the `workflowEvents` subscription, it establishes a connection to our GraphQL server and listens for response data. Unlike a query, there is no expectation that the server will immediately process and return a response. Instead, our server only pushes data to the client when a particular event occurs on your backend (In our case, a new workflow is scheduled/ran).

---

## Executing a Subscriber

Apollo provides us with different hooks like `useQuery`, `useSubscription`, etc to make our life easier when executing queries from a GraphQL server. We'll be executing a subscription we defined earlier to listen to the event and keep our frontend updated with any data changes from the backend once we are subscribed to that particular workflow.

Whenever a query returns a result in Apollo Client, that result includes a `subscribeToMore` function. You can use this function to execute a followup subscription that pushes updates to the query's original result.

```
const { subscribeToMore, data, error } = useQuery(
    WORKFLOW_DETAILS,
    {
      variables: { projectID: selectedProjectID },
      fetchPolicy: 'cache-and-network',
    }
  );

  // Using subscription to get realtime data
  
  subscribeToMore({
      document: WORKFLOW_EVENTS,
      variables: { projectID: selectedProjectID },
      updateQuery: (prev, { subscriptionData }) => {
        if (!subscriptionData.data) return prev;
        const modifiedWorkflows = prev.getWorkFlowRuns.slice();
        
        return { ...prev, getWorkFlowRuns: modifiedWorkflows };
      },
  });

```

All this subscription is doing is query the data at the very beginning and then listening to the server for any more updates on the same Workflow Event, if new data is populated the subscriber updates the previous data with the new one.

> The `subscribeToMore` function is similar in structure to the `fetchMore` function that's commonly used for handling pagination. The primary difference is that `fetchMore` executes a followup query, whereas `subscribeToMore` executes a subscription.

---

## Setting up the transport

Because subscriptions maintain a persistent connection, they can't use the default HTTP transport that Apollo Client uses for queries and mutations. Instead, Apollo Client subscriptions most commonly communicate over WebSocket, via the community-maintained `subscriptions-transport-ws` library.

We'd require `subscriptions-transport-ws` since it handles GraphQL WebSocket server and client to facilitate GraphQL queries, mutations and subscriptions over WebSocket behind the hood, other than that we'd require the `@apollo/client` library which can basically handle all your apollo related tasks quite smoothly and it also eliminates the chance of having version collision when trying out hooks/features from other community-driven libraries.

```
npm install @apollo/client subscriptions-transport-ws
```

Import and initialize a `WebSocketLink` object in the same project file where you initialize `ApolloClient`

```bash
import { WebSocketLink } from '@apollo/client/link/ws';

const wsLink = new WebSocketLink({
  uri: `ws:<GraphQL Endpoint>`,
  options: {
    reconnect: true,
    lazy: true
  }
});
```

In our project, our use case allows us to handle both HTTP as well as WebSocket requests thus our Client Root looks a bit different.

```bash
import {
  ApolloClient,
  ApolloProvider,
  HttpLink,
  InMemoryCache,
  split,
} from '@apollo/client';
import { setContext } from '@apollo/client/link/context';
import { WebSocketLink } from '@apollo/client/link/ws';
import { getMainDefinition } from '@apollo/client/utilities';
import * as React from 'react';
import { Provider } from 'react-redux';
import { PersistGate } from 'redux-persist/integration/react';
import config from './config'; // Stores the GraphQL Configuration
import App from './containers/app/App';
import configureStore from './redux/configureStore';
import getToken from './utils/getToken';

const { persistor, store } = configureStore();

// HTTP Link
const httpLink = new HttpLink({
  uri: `${config.grahqlEndpoint}/query`,
});

// Adds Authentication Headers on HTTP as well as was requests
const authLink = setContext((_, { headers }) => {
  const token = getToken();
  return {
    headers: {
      ...headers,
      authorization: token,
    },
  };
});

// WebSocket Link
const wsLink = new WebSocketLink({
  uri: `${config.grahqlEndpointSubscription}/query`,
  options: {
    reconnect: true,
    lazy: true,
  },
});

// Send query request based on the type definition
const link = split(
  ({ query }) => {
    const definition = getMainDefinition(query);
    return (
      definition.kind === 'OperationDefinition' &&
      definition.operation === 'subscription'
    );
  },
  authLink.concat(wsLink),
  authLink.concat(httpLink)
);

// Apollo Client
export const client = new ApolloClient({
  link,
  cache: new InMemoryCache(),
});

const ReduxRoot = () => {
  return (
    <ApolloProvider client={client}>
      <Provider store={store}>
        <PersistGate persistor={persistor}>
          <App />
        </PersistGate>
      </Provider>
    </ApolloProvider>
  );
};

export default ReduxRoot;
```

Using this logic, queries and mutations will use HTTP as normal, and subscriptions will use WebSocket.

## Conclusion

You can of-course extend these configurations and play around to create something out of your own. We welcome everyone to comment and let us know about what/how we can improve to achieve more! Every suggestion is appreciated.

<p align="center">
<img src="https://external-content.duckduckgo.com/iu/?u=http%3A%2F%2Fi.huffpost.com%2Fgen%2F1361346%2Fimages%2Fo-HANDWRITTEN-THANK-YOU-NOTE-facebook.jpg&f=1&nofb=1" width="1000" />
</p>

Are you an [SRE](https://en.wikipedia.org/wiki/Site_Reliability_Engineering) or a [Kubernetes](https://kubernetes.io/) enthusiast? Does [Chaos Engineering](https://en.wikipedia.org/wiki/Chaos_engineering) excite you?
Join Our Community On Slack For Detailed Discussion,

To join our slack please follow the following steps!
**Step 1:** Join the [Kubernetes slack](https://slack.k8s.io/)
**Step 2:** Join the **#litmus** channel on the Kubernetes slack or use [this link](https://slack.litmuschaos.io/) after joining the Kubernetes slack

<p align="center">
  <img src="https://i.ibb.co/XtpJ3Wz/Litmus.png" width="1500" />

Check out the [Litmus Chaos GitHub repo](https://github.com/litmuschaos/litmus) and do share your feedback: https://github.com/litmuschaos/litmus
Submit a [pull request](https://github.com/litmuschaos/litmus/pulls) if you identify any necessary changes.

Don't forget to share these resources with someone who you think might benefit from these. Peace out. ✌🏼