Labs ICT
Pro Login

Real-Time Subscriptions

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,
});