top of page
Abdul Wahith

Real-Time Data Synchronization with React and GraphQL Subscriptions


Illustration depicting real-time data synchronization with React and GraphQL subscriptions, demonstrating instant updates and seamless integration for enhanced user interaction.


In modern web applications, real-time data synchronization is crucial for providing a seamless user experience. Whether it's updating live scores, chat applications, or collaborative tools, real-time updates ensure users always have the most current information. In this blog post, we'll explore how to achieve real-time data synchronization in React using GraphQL subscriptions.


What are GraphQL Subscriptions?


GraphQL subscriptions are a way to push data from the server to the client in real-time. Unlike queries and mutations, which are request-response operations, subscriptions maintain a persistent connection (usually via WebSockets) between the client and the server. This allows the server to send updates to the client as soon as data changes.


Why Use GraphQL Subscriptions?


  1. Real-Time Updates: Subscriptions allow your application to receive updates instantly, without polling the server.

  2. Efficiency: Only relevant data changes are sent to the client, reducing the need for frequent data fetching.

  3. User Experience: Real-time synchronization enhances the interactivity and responsiveness of your application.


Setting Up GraphQL Subscriptions in React


To implement GraphQL subscriptions in a React application, we'll use the following tools:


  1. Apollo Client: A popular GraphQL client for managing data fetching in React.

  2. Apollo Server: A GraphQL server that supports subscriptions.

  3. WebSockets: A protocol for full-duplex communication channels over a single TCP connection.


Step 1: Setting Up the Server


First, let's set up an Apollo Server with subscription support.


Install Dependencies


npm install apollo-server graphql
npm install subscriptions-transport-ws graphql-subscriptions

Create the Server


Create a new file server.js and add the following code:



const { ApolloServer, gql, PubSub } = require('apollo-server');
const { createServer } = require('http');
const { execute, subscribe } = require('graphql');
const { SubscriptionServer } = require('subscriptions-transport-ws');

const pubsub = new PubSub();

const typeDefs = gql`
  type Message {
    id: ID!
    content: String!
  }

  type Query {
    messages: [Message!]
  }

  type Mutation {
    sendMessage(content: String!): Message!
  }

  type Subscription {
    messageSent: Message!
  }
`;

let messages = [];

const resolvers = {
  Query: {
    messages: () => messages,
  },
  Mutation: {
    sendMessage: (_, { content }) => {
      const message = { id: messages.length + 1, content };
      messages.push(message);
      pubsub.publish('MESSAGE_SENT', { messageSent: message });
      return message;
    },
  },
  Subscription: {
    messageSent: {
      subscribe: () => pubsub.asyncIterator(['MESSAGE_SENT']),
    },
  },
};

const server = new ApolloServer({ typeDefs, resolvers });

const httpServer = createServer(server);
server.installSubscriptionHandlers(httpServer);

httpServer.listen(4000, () => {
  console.log(`🚀 Server ready at http://localhost:4000${server.graphqlPath}`);
  console.log(`🚀 Subscriptions ready at ws://localhost:4000${server.subscriptionsPath}`);
});

Step 2: Setting Up the Client


Next, let's set up the React client to use Apollo Client for GraphQL queries, mutations, and subscriptions.


Install Dependencies


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

Create Apollo Client


Create a new file ApolloClient.js and add the following code:



import { ApolloClient, InMemoryCache, ApolloProvider, split, HttpLink } from '@apollo/client';
import { WebSocketLink } from 'apollo-link-ws';
import { getMainDefinition } from '@apollo/client/utilities';

const httpLink = new HttpLink({
  uri: 'http://localhost:4000/graphql',
});

const wsLink = new WebSocketLink({
  uri: 'ws://localhost:4000/graphql',
  options: {
    reconnect: true,
  },
});

const splitLink = split(
  ({ query }) => {
    const definition = getMainDefinition(query);
    return (
      definition.kind === 'OperationDefinition' &&
      definition.operation === 'subscription'
    );
  },
  wsLink,
  httpLink
);

const client = new ApolloClient({
  link: splitLink,
  cache: new InMemoryCache(),
});

export default client;

Set Up the React App


In your main application file, wrap your app with the ApolloProvider and use the Apollo client.



import React from 'react';
import ReactDOM from 'react-dom';
import { ApolloProvider } from '@apollo/client';
import client from './ApolloClient';
import App from './App';

ReactDOM.render(
  <ApolloProvider client={client}>
    <App />
  </ApolloProvider>,
  document.getElementById('root')
);

Create the Subscription Component


Now, create a component that subscribes to new messages.



import React from 'react';
import { useQuery, useMutation, useSubscription, gql } from '@apollo/client';

const GET_MESSAGES = gql`
  query GetMessages {
    messages {
      id
      content
    }
  }
`;

const SEND_MESSAGE = gql`
  mutation SendMessage($content: String!) {
    sendMessage(content: $content) {
      id
      content
    }
  }
`;

const MESSAGE_SENT = gql`
  subscription OnMessageSent {
    messageSent {
      id
      content
    }
  }
`;

const Chat = () => {
  const { data, loading } = useQuery(GET_MESSAGES);
  const [sendMessage] = useMutation(SEND_MESSAGE);
  const { data: subscriptionData } = useSubscription(MESSAGE_SENT);

  if (loading) return <p>Loading...</p>;

  return (
    <div>
      <h1>Chat</h1>
      <ul>
        {data.messages.map((message) => (
          <li key={message.id}>{message.content}</li>
        ))}
        {subscriptionData && (
          <li key={subscriptionData.messageSent.id}>{subscriptionData.messageSent.content}</li>
        )}
      </ul>
      <input
        type="text"
        onKeyDown={(e) => {
          if (e.key === 'Enter') {
            sendMessage({ variables: { content: e.target.value } });
            e.target.value = '';
          }
        }}
      />
    </div>
  );
};

export default Chat;

Step 3: Running the Application


To run your application, start both the server and the client. In the server directory, run:


node server.js

In the client directory, run:


npm start

Now, open your browser and navigate to http://localhost:3000. You should see your chat application, where new messages appear in real-time without requiring a page refresh.


Conclusion


Real-time data synchronization is essential for modern web applications, and GraphQL subscriptions provide a robust and efficient way to implement it in React. By using Apollo Client and Apollo Server, you can easily set up subscriptions to push real-time updates to your React components.


This guide covers the basics of setting up real-time data synchronization with React and GraphQL subscriptions, but there's much more to explore. You can extend this implementation to handle more complex scenarios, optimize performance, and integrate with other technologies.

15 views0 comments

Comments


bottom of page