In today’s fast-paced world, real-time data is becoming a necessity for modern applications. From financial tickers to social media updates, users demand fresh data without the need to refresh pages. This is where technologies like GraphQL subscriptions and WebSockets come into play. In this article, we will explore how to implement real-time data subscriptions using GraphQL and WebSocket consumers. Along the way, we’ll examine how these two technologies work together to deliver server-side updates instantly to clients.
What is GraphQL?
GraphQL is a query language for APIs that allows clients to request only the data they need. Unlike traditional REST APIs, GraphQL gives clients the flexibility to specify exactly what data they want. This makes it more efficient in terms of network performance. Moreover, GraphQL provides three types of operations:
- Queries: Fetch data from the server.
- Mutations: Modify data on the server.
- Subscriptions: Automatically receive real-time updates from the server.
Subscriptions enable real-time communication, making them a perfect fit for applications that require constant updates.
Understanding WebSockets
WebSockets are a protocol that enables two-way communication between the client and the server. Unlike HTTP, which is request-response-based, WebSockets allow the server to push updates to clients without requiring them to make explicit requests.
In a typical HTTP setup, clients have to repeatedly send requests to check if there is any new data on the server. This is called polling and can be inefficient. In contrast, WebSockets create a persistent connection between the client and the server. As a result, the server can send data as soon as it becomes available, thus reducing latency and improving the overall performance of the application.
Why Use GraphQL Subscriptions with WebSockets?
GraphQL subscriptions and WebSockets complement each other. Subscriptions are designed to notify clients about server-side updates, and WebSockets provide the transport layer to deliver these updates in real-time. By combining these two technologies, you can build applications that provide instant feedback and dynamic content updates without excessive bandwidth usage.
Some use cases for GraphQL subscriptions with WebSockets include:
- Chat applications: Real-time messaging where users can receive new messages without refreshing.
- Online gaming: Updates on game status and player movements.
- Collaborative tools: Live editing and updates in shared documents or spreadsheets.
- Financial applications: Stock market price updates and real-time trades.
Setting Up GraphQL Subscriptions with WebSockets
Let’s dive into a practical example of setting up GraphQL subscriptions using WebSockets. We will create a simple system where users can subscribe to receive real-time updates about a stock price.
Install Necessary Libraries
To begin, we need to set up a Node.js environment and install the necessary dependencies. We’ll be using Apollo Server
for the GraphQL backend and graphql-ws
for managing WebSocket connections.
Run the following command in your terminal to install the required packages:
npm init -y
npm install apollo-server graphql graphql-ws ws
Define the GraphQL Schema
In our GraphQL schema, we will define a StockPrice
type and a subscription for tracking real-time stock price updates. The schema below includes the subscription
keyword, which is crucial for enabling real-time updates.
const { gql } = require('apollo-server');
const typeDefs = gql`
type StockPrice {
symbol: String!
price: Float!
}
type Query {
stocks: [StockPrice]
}
type Subscription {
stockPriceUpdated(symbol: String!): StockPrice
}
`;
module.exports = typeDefs;
Here, we define a StockPrice
type that includes a symbol
(e.g., “AAPL” for Apple) and its associated price
. The stockPriceUpdated
subscription accepts a symbol
argument and returns updates about the stock’s price.
Implement the Resolvers
The resolver for a GraphQL subscription is different from those for queries and mutations. Instead of returning data immediately, it opens a long-lived connection that sends data whenever the server pushes new updates.
const { PubSub } = require('apollo-server');
const pubsub = new PubSub();
const resolvers = {Query: {
stocks: () => {
return [
{ symbol: ‘AAPL’, price: 150 },
{ symbol: ‘GOOG’, price: 2800 },
];
},
},
Subscription: {
stockPriceUpdated: {
subscribe: (_, { symbol }) => pubsub.asyncIterator([`STOCK_PRICE_${symbol}`]),
},
},
};
module.exports = resolvers;In the resolver, we use the PubSub
class to manage subscriptions. The asyncIterator
method listens for changes on the specified stock symbol (e.g., STOCK_PRICE_AAPL
). When the price of Apple stock changes, the server will notify all subscribed clients.
Start the Apollo Server
Now that we have defined our schema and resolvers, we can create the Apollo Server instance and start listening for both HTTP and WebSocket connections.
const { ApolloServer } = require('apollo-server');
const typeDefs = require('./schema');
const resolvers = require('./resolvers');
const server = new ApolloServer({typeDefs,
resolvers,
subscriptions: {
path: ‘/subscriptions’,
},
});
server.listen().then(({ url, subscriptionsUrl }) => {console.log(`🚀 Server ready at ${url}`);
console.log(`🚀 Subscriptions ready at ${subscriptionsUrl}`);
});
This sets up an Apollo Server with support for WebSocket subscriptions on the /subscriptions
endpoint.
Simulate Stock Price Updates
To make the example complete, we need to simulate stock price changes and publish updates via the PubSub
system.
const { PubSub } = require('apollo-server');
const pubsub = new PubSub();
const simulateStockPriceUpdates = () => {setInterval(() => {
const symbols = [‘AAPL’, ‘GOOG’];
symbols.forEach((symbol) => {
const newPrice = Math.random() * 1000;
pubsub.publish(`STOCK_PRICE_${symbol}`, {
stockPriceUpdated: { symbol, price: newPrice },
});
});
}, 5000);
};
simulateStockPriceUpdates();This script runs every 5 seconds, generating random prices for the stocks “AAPL” and “GOOG”. The new prices are published through the PubSub
system, and any clients subscribed to these stock symbols will receive the updated prices immediately.
Test the Subscription with a Client
You can use a tool like GraphQL Playground or Apollo Studio to test the subscription. Here’s an example query to subscribe to Apple’s stock price updates:
subscription {
stockPriceUpdated(symbol: "AAPL") {
symbol
price
}
}
Once subscribed, you should start receiving real-time updates every 5 seconds as the stock price changes.
Handling WebSocket Connections on the Client
To connect to the WebSocket server and listen for real-time updates, you need to set up a client-side WebSocket handler. Here’s an example using Apollo Client
in JavaScript:
import { ApolloClient, InMemoryCache } from '@apollo/client';
import { WebSocketLink } from '@apollo/client/link/ws';
import { split, HttpLink } from '@apollo/client';
import { getMainDefinition } from '@apollo/client/utilities';
// Create an HTTP link for queries and mutationsconst httpLink = new HttpLink({
uri: ‘http://localhost:4000/graphql’,
});
// Create a WebSocket link for subscriptionsconst wsLink = new WebSocketLink({
uri: ‘ws://localhost:4000/subscriptions’,
options: {
reconnect: true,
},
});
// Split traffic based on operation typeconst link = split(
({ query }) => {
const definition = getMainDefinition(query);
return definition.kind === ‘OperationDefinition’ && definition.operation === ‘subscription’;
},
wsLink,
httpLink
);
// Set up Apollo Clientconst client = new ApolloClient({
link,
cache: new InMemoryCache(),
});
export default client;Conclusion
In this article, we explored how to set up real-time data subscriptions using GraphQL and WebSockets. By leveraging the power of WebSocket connections, we were able to create a seamless system for pushing server-side updates directly to the client. This technique is invaluable for creating applications that require real-time updates, such as live feeds, messaging apps, collaborative tools, and financial dashboards.
By using GraphQL subscriptions with WebSockets, you can deliver responsive, low-latency updates to users, improving the overall experience of your application. Furthermore, the approach is scalable and can be integrated into existing GraphQL APIs with minimal overhead.
Now that you have a basic understanding of how to implement GraphQL subscriptions using WebSockets, you can start experimenting and building real-time features into your own applications.