"Mastering GraphQL Subscriptions: Unlock Real-Time Data Updates with Ease"


GraphQL subscriptions enable real-time communication between the client and server. They allow clients to listen for server events, making them ideal for scenarios like chat applications, live updates, or notifications.
In this article, we’ll walk through building a GraphQL subscription, explain the code in detail, and demonstrate how real-time updates work.
What Are Subscriptions?
Subscriptions: A feature in GraphQL that allows clients to listen for specific events from the server.
Real-time Communication: Subscriptions use WebSockets to maintain an active connection, allowing the server to push updates to the client as they occur.
Example:
subscription {
newMessage {
id
content
sender
}
}
This subscription listens for newMessage
events and returns each new message's id
, content
, and sender
.
Setting Up Subscriptions in GraphQL
Step 1: Define the Schema
We begin by defining the schema in a schema.graphql
file.
type Message {
id: ID!
content: String!
sender: String!
}
type Subscription {
newMessage: Message!
}
type Mutation {
sendMessage(content: String!, sender: String!): Message!
}
Explanation:
Message
Type: Defines the structure of a message.id
: A unique identifier for the message.content
: The text of the message.sender
: The user who sent the message.
Subscription
Type: Contains thenewMessage
field, which will emit real-time updates when a new message is sent.Mutation
Type: ThesendMessage
mutation allows a client to send a message. Each mutation triggers thenewMessage
subscription.
Step 2: Set Up Resolvers
In GraphQL, resolvers define how the API handles queries, mutations, and subscriptions.
Subscription Resolver
The newMessage
resolver handles real-time updates. Each client gets its own channel to receive updates.
func (r *Resolver) NewMessage(ctx context.Context) (<-chan *Message, error) {
r.mu.Lock()
defer r.mu.Unlock()
// Create a channel for the subscriber
messageChannel := make(chan *Message, 1)
subscriberID := fmt.Sprintf("subscriber-%d", len(r.messageSubscribers)+1)
r.messageSubscribers[subscriberID] = messageChannel
// Cleanup channel on subscription termination
go func() {
<-ctx.Done()
r.mu.Lock()
delete(r.messageSubscribers, subscriberID)
r.mu.Unlock()
}()
return messageChannel, nil
}
Detailed Explanation:
Channel Creation:
messageChannel
: A dedicated channel for each subscriber. It allows the server to send real-time updates.subscriberID
: A unique identifier for each subscription.
Subscription Registry:
- Adds the subscriber’s channel to the
messageSubscribers
map.
- Adds the subscriber’s channel to the
Context Cleanup:
- Listens for the subscription to end (
ctx.Done()
) and removes the subscriber from the registry.
- Listens for the subscription to end (
Return Channel:
- Returns the
messageChannel
to send real-time updates to the client.
- Returns the
Mutation Resolver
The sendMessage
resolver handles creating new messages and notifying subscribers.
func (r *Resolver) SendMessage(ctx context.Context, content string, sender string) (*Message, error) {
message := &Message{
ID: uuid.NewString(),
Content: content,
Sender: sender,
}
// Notify all subscribers
r.mu.Lock()
for _, subscriber := range r.messageSubscribers {
subscriber <- message
}
r.mu.Unlock()
return message, nil
}
Detailed Explanation:
Message Creation:
uuid.NewString()
: Generates a unique ID for the message.content
andsender
: Populated from the mutation input fields.
Broadcast Logic:
Iterates through all subscribers in
messageSubscribers
.Sends the new message to each subscriber’s channel.
Thread Safety:
Return Message:
- Returns the newly created message as a response to the mutation.
Step 3: Connect the Server
Update the server to handle subscriptions.
package main
import (
"log"
"net/http"
"github.com/99designs/gqlgen/graphql/handler"
"github.com/99designs/gqlgen/graphql/playground"
)
func main() {
resolver := NewResolver()
srv := handler.NewDefaultServer(
generated.NewExecutableSchema(generated.Config{
Resolvers: resolver,
}),
)
http.Handle("/", playground.Handler("GraphQL Playground", "/query"))
http.Handle("/query", srv)
log.Println("Server running at http://localhost:8080/")
log.Fatal(http.ListenAndServe(":8080", nil))
}
Explanation:
Resolver Setup:
NewResolver()
: Initializes the resolver with a registry for managing subscriptions.
GraphQL Playground:
- Provides an interactive interface for testing queries, mutations, and subscriptions.
WebSocket Support:
srv
: Handles HTTP and WebSocket connections for real-time updates.
Step 4: Test Subscriptions
4.1: Start a Subscription
Open GraphQL Playground and enter the subscription query:
subscription {
newMessage {
id
content
sender
}
}
4.2: Trigger a Mutation
In another tab, send a mutation to add a new message:
mutation {
sendMessage(content: "Hello, World!", sender: "Alice") {
id
content
sender
}
}
4.3: View Real-Time Update
The first tab (subscription) will display:
{
"data": {
"newMessage": {
"id": "1",
"content": "Hello, World!",
"sender": "Alice"
}
}
}
Explanation:
The mutation triggers the
sendMessage
resolver, which notifies all subscribers through their channels.The
newMessage
subscription sends the updated data to the client in real-time.
Key Concepts Recap
Subscriptions:
- Use WebSockets to provide real-time updates to clients.
Resolvers:
NewMessage
: Manages subscribers and broadcasts updates.SendMessage
: Handles message creation and triggers notifications.
Channel-Based Communication:
Each subscriber gets a unique channel for updates.
The server broadcasts events to all active channels.
Conclusion
GraphQL subscriptions are a powerful tool for implementing real-time updates in your applications. By following this guide:
You can create a basic subscription system.
Extend it to more complex use cases, like notifications or live dashboards.
Subscriptions open the door to dynamic and interactive user experiences. Start experimenting to bring real-time features to your GraphQL APIs!
Subscribe to my newsletter
Read articles from Shivam Dubey directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
