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
- Filter subscriptions: Only needed data
- Scalability: Use Redis for distributed systems
- Connection limits: Manage active subscriptions
- Error handling: Graceful disconnection
- 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.