Isaac.

GraphQL Subscriptions

Implement real-time data streaming with GraphQL subscriptions.

By EMEPublished: February 20, 2025
graphqlsubscriptionsreal-timewebsockets

A Simple Analogy

GraphQL subscriptions are like a news feed. Instead of constantly asking for updates, you subscribe and get notifications when new content arrives.


Why Subscriptions?

  • Real-time: Push data instantly
  • Efficient: Only send changed data
  • Bidirectional: Client and server communicate
  • Scalable: WebSocket-based
  • Reactive: Respond to events

Schema Definition

type Subscription {
  orderCreated: Order!
  orderUpdated(id: ID!): Order!
  messageReceived(chatId: ID!): Message!
  stockPriceChanged(symbol: String!): StockPrice!
}

type Order {
  id: ID!
  customerId: String!
  total: Float!
  status: OrderStatus!
  createdAt: String!
}

enum OrderStatus {
  PENDING
  CONFIRMED
  SHIPPED
  DELIVERED
}

Server Implementation

using HotChocolate;
using HotChocolate.Execution.Configuration;
using HotChocolate.Subscriptions;

public class Subscription
{
    [Subscribe]
    [Topic("OrderCreated")]
    public Order OnOrderCreated(
        [EventMessage] Order order) => order;
    
    [Subscribe(With = nameof(SubscribeToOrderUpdates))]
    public Order OnOrderUpdated(
        string id,
        [EventMessage] Order order) => order;
    
    public IAsyncEnumerable<Order> SubscribeToOrderUpdates(
        string id,
        [Service] ITopicEventReceiver receiver) =>
        receiver.ReceiveAsync<Order>($"orderUpdated_{id}");
}

public class OrderMutations
{
    public async Task<Order> CreateOrder(
        CreateOrderInput input,
        [Service] ITopicEventSender eventSender)
    {
        var order = new Order { /* ... */ };
        
        // Publish to subscribers
        await eventSender.SendAsync("OrderCreated", order);
        
        return order;
    }
}

Client Subscription

import { useSubscription, gql } from "@apollo/client";

const ORDER_CREATED_SUBSCRIPTION = gql`
  subscription OnOrderCreated {
    orderCreated {
      id
      customerId
      total
      status
      createdAt
    }
  }
`;

function OrderNotifications() {
  const { data, loading, error } = useSubscription(ORDER_CREATED_SUBSCRIPTION);
  
  if (loading) return <div>Waiting for orders...</div>;
  if (error) return <div>Error: {error.message}</div>;
  
  return (
    <div>
      <h3>New Order</h3>
      <p>Order #{data.orderCreated.id}</p>
      <p>Total: ${data.orderCreated.total}</p>
      <p>Status: {data.orderCreated.status}</p>
    </div>
  );
}

WebSocket Configuration

builder.Services
    .AddGraphQLServer()
    .AddQueryType<Query>()
    .AddMutationType<Mutation>()
    .AddSubscriptionType<Subscription>()
    .AddInMemorySubscriptions()
    .AddWebSocketProtocol();

app.UseWebSockets();
app.MapGraphQL("/graphql");

Broadcasting Events

public class OrderService
{
    private readonly ITopicEventSender _eventSender;
    
    public OrderService(ITopicEventSender eventSender)
    {
        _eventSender = eventSender;
    }
    
    public async Task<Order> CreateOrderAsync(CreateOrderInput input)
    {
        var order = new Order { /* ... */ };
        
        // Broadcast to all subscribers of "OrderCreated"
        await _eventSender.SendAsync("OrderCreated", order);
        
        return order;
    }
    
    public async Task<Order> UpdateOrderAsync(string id, UpdateOrderInput input)
    {
        var order = new Order { /* ... */ };
        
        // Broadcast to specific order subscribers
        await _eventSender.SendAsync($"orderUpdated_{id}", order);
        
        return order;
    }
}

Best Practices

  1. Filter subscriptions: Only needed data
  2. Scalability: Use Redis for distributed systems
  3. Connection limits: Manage active subscriptions
  4. Error handling: Graceful disconnection
  5. Security: Authenticate subscription requests

Related Concepts

  • WebSocket protocols
  • Server-sent events (SSE)
  • Redis pub/sub
  • Message queues

Summary

GraphQL subscriptions provide real-time, bidirectional communication between client and server. Use them for notifications, live updates, and collaborative features.