XenonStack Recommends

Service Design

GraphQL with WebSocket Protocol and Subscriber | Complete Guide

Navdeep Singh Gill | 05 Dec 2022

Introduction GraphQL with WebSocket

GraphQL is a great way to write customizable API's and combine numerous services into one endpoint. Mainly, the idea has a single endpoint, but getting the different combinations of models is amazing, mostly for the organizations working with large-scale multi-platform applications with various developers and designers.

What is a Web-Socket?

According to MDN, " The WebSocket API is an advanced technology that makes it possible to open a two-way interactive communication session between the user's browser and a server. With this API, you can send messages to a server and receive event-driven responses without having to poll the server for a reply."
Real-time data, Connections, Scalability, fan-out and broadcasting are all handled by Intelligent Client Libraries and AppSync. Source: New features that will enhance your Real-Time experience
WebSocket is a protocol that gives a way to exchange data between browsers and servers over a persistent connection. It's very beneficial for applications that need continuous data exchange, for example- real-time systems, online games, and many more. A new WebSocket connection is opened by using a special WebSocket protocol in the regular URL.

What is a GraphQL Subscription?

Subscriptions are a technique to fetch data, just like queries. But, subscriptions maintain an active connection to the GraphQL server through WebSocket. This allows the server to push updates to the subscription's result over time. They are very helpful in notifying the client about any updates to the real-time requests. Whenever the backend data changes, the client will automatically get notified through the active server connection.

How to implement?

Here Apollo Client comes into the picture. The Apollo Client is a comprehensive state management library for JavaScript, enabling a user to organize both local and remote data with GraphQL. It is used to fetch, modify, and cache application data while automatically updating the user interface. The core @apollo/client library gives built-in integration with React js, and the larger Apollo community supports integrations for the other popular view layers. Hence it is comfortable for all those developers searching to control its data in the Front-End.
A query based language for REST APIs used to retrieve the exact data needed. Click to explore about our, Apollo GraphQL Features

What is a Subscriber?

Subscriber has to be defined both on the client-side also on the server-side. But, for now, we will handle the subscriber on the client-side because server-side subscriber configuration might vary depending on projects to projects. A server-side subscriber can be served through GoLang, Node JS and many more libraries, but the client-side handling mostly remains the same for various frameworks, libraries, and tools. You can use any open source project to handle client-side subscriptions with Apollo, but here in this, I will use an open-source project named LitmusChaos.

Defining a Subscriber

In this project, we are going to deal with various Workflow Data. We have set up the Subscriber in such a way that we can listen to the Workflow Events and receive several necessary parameters that are required for Workflow Representation Data and many more. 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   } }`; Whenever an Apollo Client performs the workflow events subscription, it connects to the GraphQL server and listens for response data. But like a query, it doesn't anticipate that the server will operate at once and return a response. Instead, our server only pushes data to the client whenever a particular event takes place at the backend.

Subscriber Execution

Apollo gives us various hooks like Subscription, user query, and many more, making life more comfortable while performing and executing queries from a GraphQL server. Here we will be executing a previously defined subscription to listen for an event and keep the frontend up to date with any data changes at the backend when we are subscribed in that respective workflow. In Apollo Client, whenever a query returns a result, it contains a subscriber more function. const { subscribeToMore, error, data } = useQuery( WORKFLOW_DETAILS,  {    variables: { projectID: selectedProjectID },    fetchPolicy: 'cache-and-network',  } );   // Using subscription to get real-time 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 };    }, }); This subscription is making a query of the data at the start and then listening to the server for further updates on the same Workflow Event if any new data is generated. Hence the subscriber updates the old data with the latest information is generated.

Setting the transport

Subscriptions keep a constant connection. Accordingly, Apollo Client subscriptions regularly communicate over WebSocket through the community-maintained subscriptions-transport-ws library. Here we need to install some libraries.   npm install @apollo/client subscriptions-transport-ws Then import and initialize a WebSocketLink where the ApolloClient is initialized before. import { WebSocketLink } from '@apollo/client/link/ws'; const wsLink = new WebSocketLink({  uri: `ws:<GraphQL Endpoint>`,  options: {    reconnect: true,    lazy: true  } }); In this project, our use case allows us to handle both WebSocket and HTTP requests hence our code look like. 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 type   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;
The fusion of digital technology into all industry sides, changing how you use and convey it to customers. Download to explore the potential of Digital Transformation


Using this logic, mutations and queries will use HTTP as normal, and subscriptions will use WebSocket. There is no limitation, and you can extend this configuration and create anything your like. Do check this doc for more information.