Real-Time Subscriptions
Subscriptions let clients receive real-time updates when data changes on the server. Unlike queries and mutations which are request-response, subscriptions maintain a persistent connection.
They're perfect for chat apps, live notifications, dashboards, and any feature that needs instant updates.
How Subscriptions Work
Subscriptions typically use WebSockets to maintain a connection between client and server. When something changes, the server pushes the update to all connected clients.
// Schema
type Subscription {
messageAdded: Message!
userStatusChanged(userId: ID!): UserStatus!
}
type Message {
id: ID!
text: String!
author: User!
createdAt: String!
}
// Client subscribes to new messages
subscription {
messageAdded {
id
text
author {
name
}
}
}
The subscription stays active and receives data whenever a new message is added.
Server Implementation
To implement subscriptions, you need a pubsub system to manage events:
const { PubSub } = require('graphql-subscriptions');
const pubsub = new PubSub();
const resolvers = {
Mutation: {
sendMessage: async (parent, args, context) => {
const message = await context.db.messages.create({
text: args.text,
authorId: context.user.id,
});
// Publish the event
pubsub.publish('MESSAGE_ADDED', { messageAdded: message });
return message;
},
},
Subscription: {
messageAdded: {
subscribe: () => pubsub.asyncIterator(['MESSAGE_ADDED']),
},
},
};
When a message is created, the mutation publishes an event. All subscribed clients receive the update.
Client-Side Subscriptions
On the client, use the useSubscription hook to listen for updates:
import { useSubscription, gql } from '@apollo/client';
const MESSAGE_SUBSCRIPTION = gql`
subscription OnMessageAdded {
messageAdded {
id
text
author {
name
}
}
}
`;
function MessageList() {
const { data, loading } = useSubscription(MESSAGE_SUBSCRIPTION);
// When data arrives, add it to your list
useEffect(() => {
if (data) {
setMessages(prev => [...prev, data.messageAdded]);
}
}, [data]);
return (
<ul>
{messages.map(msg => (
<li key={msg.id}>{msg.author.name}: {msg.text}</li>
))}
</ul>
);
}
The useSubscription hook automatically manages the WebSocket connection.
Subscription Best Practices
Use with Authentication: Always authenticate subscription connections.
Limit Connections: Too many concurrent subscriptions can overwhelm your server.
Clean Up: Unsubscribe when components unmount to prevent memory leaks.
Handle Reconnection: WebSocket connections can drop. Implement reconnection logic.
// Subscribe with variables for filtered updates
const NOTIFICATION_SUBSCRIPTION = gql`
subscription OnNotification($userId: ID!) {
notificationReceived(userId: $userId) {
id
message
type
}
}
`;
// Only subscribe when user is logged in
const { data } = useSubscription(NOTIFICATION_SUBSCRIPTION, {
variables: { userId: currentUser?.id },
skip: !currentUser,
});