Back to Blog
Lesson 52 of the GraphQL: Your First GraphQL Schema & Server course
API ArchitectureSeptember 8, 20264 min read

Subscriptions Overview: Real-Time GraphQL and Pub/Sub

Learn how to implement real-time data updates in your API using GraphQL Subscriptions. Discover the Pub/Sub model and how to build live-updating services.

GraphQLSubscriptionsReal-timeWebSocketsPubSub
A minimalist image of a 'Subscribe' card in a green envelope on a dark background.

Previously in this course, we covered handling mutation errors to ensure our data modifications are robust and predictable. Now, we shift from request-response cycles to streaming data.

In the standard GraphQL model, the client initiates every interaction: you ask for data (Query), or you change it (Mutation). But what if your application needs to show a notification the moment a new record is created, or display a live stock price? This is where Subscriptions come in.

Understanding the Pub/Sub Model

At its core, a subscription allows the server to push data to the client over a persistent connection. Unlike HTTP, which is stateless and closes after each request, subscriptions typically use WebSockets to keep a long-lived channel open between the client and the server.

To manage this, we use the Publish/Subscribe (Pub/Sub) pattern:

  1. Publishers: Components (like a mutation resolver) that send an event when data changes.
  2. Subscribers: Clients that "listen" for specific events.
  3. Broker: A middleman (the PubSub engine) that routes messages from the publisher to all relevant subscribers.

Think of it like a newsletter: you (the subscriber) sign up to receive updates about a specific topic. When the author (the publisher) writes a new post, the distribution service (the broker) pushes that post to your inbox automatically.

Defining a Subscription Type

In GraphQL, you define a Subscription type in your schema just like you do for Query and Mutation. However, instead of returning a final value, a subscription field defines an AsyncIterator that yields data over time.

Here is a basic schema definition for a real-time message feed:

GraphQL
type Message {
  id: ID!
  content: String!
}

type Subscription {
  # The field name reflects the event the client is listening for
  messageAdded: Message!
}

When a client executes this subscription, they receive a stream of Message objects whenever a new one is added to the system.

Worked Example: Implementing a Subscription

To implement this, you need a PubSub engine. For simple local setups, we often use PubSub from the graphql-subscriptions package.

First, initialize your PubSub instance:

JAVASCRIPT
const { PubSub } = require(CE9178">'graphql-subscriptions');
const pubsub = new PubSub();

// Constants for event names
const MESSAGE_ADDED = CE9178">'MESSAGE_ADDED';

Next, update your mutation to publish an event whenever a new message is saved:

JAVASCRIPT
const resolvers = {
  Mutation: {
    addMessage: (_, { content }) => {
      const newMessage = { id: CE9178">'1', content };
      // Publish the event to the broker
      pubsub.publish(MESSAGE_ADDED, { messageAdded: newMessage });
      return newMessage;
    },
  },
  Subscription: {
    messageAdded: {
      // The subscribe method returns an AsyncIterator
      subscribe: () => pubsub.asyncIterator([MESSAGE_ADDED]),
    },
  },
};

When addMessage is called, the pubsub.publish call triggers the messageAdded subscription for any connected clients currently listening.

Hands-on Exercise

  1. Install the necessary package in your project: npm install graphql-subscriptions.
  2. Add a Subscription type to your typeDefs.
  3. Implement a postCreated subscription in your current project.
  4. Modify your existing createPost mutation to trigger pubsub.publish whenever a post is created.
  5. Test the subscription using the Apollo Sandbox (or your preferred GraphQL client that supports WebSocket protocols).

Common Pitfalls

  • Connection Overhead: Because WebSockets are stateful, you must manage connections carefully. If you have thousands of clients, you cannot simply keep them all connected to a single server instance. You'll eventually need a distributed broker like Redis, which we explore in Integrating Redis with WebSockets for Real-Time Apps.
  • Missing Subscriptions in Schema: Beginners often forget that Subscription is a root type. If you don't explicitly define it in your SDL, the server will not register it.
  • Payload Mismatch: Ensure the object you pass to pubsub.publish matches the structure expected by the Subscription resolver. If your subscription field is messageAdded, the published object must have a key named messageAdded.

FAQ

Does every GraphQL server support Subscriptions? Not out of the box. While the GraphQL specification supports them, you need a transport layer (like WebSockets) configured in your server, which Apollo Server handles automatically if set up correctly.

Can I use HTTP for subscriptions? Standard HTTP is request-response based. While you can use techniques like Long Polling or Server-Sent Events (SSE), WebSockets are the industry standard for full-duplex, real-time GraphQL communication.

Are subscriptions better than polling? Yes. Polling forces the client to constantly ask "Is there new data?" (wasting bandwidth). Subscriptions let the server tell the client "I have new data" only when an update actually occurs.

Recap

Subscriptions enable real-time features by moving away from the request-response cycle. By using the Pub/Sub model, we can decouple the event trigger (the mutation) from the event listener (the client), allowing for fluid, reactive interfaces.

Up next: We will discuss how to secure your schema by implementing field-level authorization to ensure sensitive data remains protected.

Similar Posts