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."
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 GraphQL data in the Front-End.
Read more about
Overview of GraphQL Features and Tools
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 datasubscribeToMore({ 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;
Conclusion
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.
Thanks for submitting the form.