GraphQL Subscriptions in Ruby
Lesson Overview
Welcome to this lesson on setting up subscriptions for real-time data. In this lesson, we will discuss real-time data and subscriptions to events in GraphQL using graphql-ruby and Sinatra.
GraphQL subscriptions enable clients to listen for real-time updates from the server. When an event that matches the subscription's criteria occurs, the server sends the updated data to the client automatically.
This is how subscriptions differ from Queries and Mutations:
- Queries: Request data from the server.
- Mutations: Modify data on the server.
- Subscriptions: Receive updates whenever data is changed as specified.
Setting Up Subscriptions with graphql-ruby
Let's begin by setting up our server to handle subscriptions using graphql-ruby and Sinatra. We'll need to install the necessary gems first:
We'll start by creating the basic structure for our subscription system. In graphql-ruby, subscriptions use a publish-subscribe pattern, where events are published to subscribers through triggers.
A Quick Note on GraphQL Variables
Before diving into the schema, let's introduce GraphQL variables — a concept we'll use later in our client code.
So far in this course, we've been passing argument values directly inside query strings (inline). GraphQL also supports variables, which let you define parameters separately from the query. This keeps queries reusable and avoids messy string interpolation.
Here's the syntax. The query declares the variables and their types with a $ prefix, and the actual values are passed in a separate variables object:
The ! after the type (e.g., String!) means the variable is required — the server will reject the request if it's missing. We'll use this pattern in the client code later in this lesson.
Defining Schema with Subscriptions
In graphql-ruby, we define types using Ruby classes. Let's create our Book type, Query type, Mutation type, and, importantly, our Subscription type.
The Subscription type defines a book_added field, which is of type Book. When a new book is added, all clients subscribed to book_added will receive the update.
Configuring the Schema and Subscription Backend
Now we need to create our schema and configure it to handle subscriptions. In graphql-ruby, subscriptions require a backend that manages which clients are subscribed to which events. When a mutation triggers a subscription event, this backend is responsible for delivering the update to every subscribed client.
For this example, we'll build a simple in-memory subscription backend. The GraphQL::Subscriptions base class requires us to implement several methods, each with a specific role:
Now define the schema, tying together our query, mutation, and subscription types, and configuring it to use our in-memory subscription backend:
Here, when a book is added using the add_book mutation, the new book data is sent to all clients subscribing to the book_added subscription through the MySchema.subscriptions.trigger method.
Integrating WebSocket for Real-Time Updates
WebSockets provide a way for a server and a client to communicate in real time over a single, long-lived connection. Unlike regular HTTP requests (which follow a request-response pattern), a WebSocket connection stays open, allowing the server to push data to the client at any time. This is crucial for handling subscriptions.
We'll integrate WebSockets into our Sinatra application to handle subscriptions. The server and client communicate using a simple message protocol with three message types:
'start'— sent by the client to begin a subscription, including the GraphQL query as its payload.'data'— sent by the server to deliver results (both initial responses and real-time updates).'stop'— sent by the client to unsubscribe and stop receiving updates.
When you run this code with ruby server.rb, your server should be ready to handle real-time subscriptions at ws://localhost:4000/graphql.
Requesting Subscriptions after Setting Up the Server
After setting up the server to handle subscriptions, it's essential to know how to request and subscribe to real-time data updates. Below, we will provide step-by-step instructions for setting up a client to request subscriptions.
First, install the required gems for the client:
Create a client file to test subscriptions:
GraphQL Queries and Mutations
Define the queries and mutations that will be used in the client application. Note that ADD_BOOK_MUTATION uses GraphQL variables ($title and $author) as introduced earlier in this lesson, keeping the query reusable:
Helper Function for Sending GraphQL Requests
Create a function to facilitate sending GraphQL requests using net/http:
Setting Up WebSocket for Subscriptions
Initialize the WebSocket client and set up the subscription for real-time updates. We use EventMachine (EM), a Ruby library that provides an event-driven I/O loop. WebSocket connections are long-lived and asynchronous — the client needs to stay running and react to incoming messages at any time. EventMachine provides this event loop, letting our code register callbacks (like on :open and on :message) that fire when events occur, rather than blocking in a linear flow:
Executing Queries and Mutations
Execute the defined queries and mutations:
This client code demonstrates how to:
- Query existing books.
- Add a new book via mutation.
- Subscribe to real-time updates when books are added.
Summary
In this lesson, we:
- Discussed real-time data and its importance.
- Introduced GraphQL subscriptions and compared them with
QueriesandMutations. - Introduced GraphQL variables for parameterized queries.
- Set up
graphql-rubyandSinatrawith subscriptions. - Defined schema and configured a subscription backend using Ruby classes.
- Integrated WebSockets with EventMachine for real-time updates.
You're now ready to move on to the practice exercises. These will help you solidify your understanding by applying what you've learned hands-on.
