graphql-ruby-client includes three kinds of support for subscriptions with Relay Modern:
To use it, require graphql-ruby-client/subscriptions/createRelaySubscriptionHandler and call the function with your client and optionally, your OperationStoreClient.
Note: For Relay <11, use import { createLegacyRelaySubscriptionHandler } from "graphql-ruby-client/subscriptions/createRelaySubscriptionHandler" instead; the signature changed in Relay 11.
See the Subscriptions guide for information about server-side setup.
Subscriptions with Pusher require two things:
pusher-js libraryfetchOperation function for sending the subscription operation to the serverPass pusher: to get Subscription updates over Pusher:
// Load the helper function import createRelaySubscriptionHandler from "graphql-ruby-client/subscriptions/createRelaySubscriptionHandler" // Prepare a Pusher client var Pusher = require("pusher-js") var pusherClient = new Pusher(appKey, options) // Create a fetchOperation, see below for more details function fetchOperation(operation, variables, cacheConfig) { return fetch(...) } // Create a Relay Modern-compatible handler var subscriptionHandler = createRelaySubscriptionHandler({ pusher: pusherClient, fetchOperation: fetchOperation }) // Create a Relay Modern network with the handler var network = Network.create(fetchQuery, subscriptionHandler) If you’re using compressed payloads, configure a decompress: function, too:
// Add `pako` to the project for gunzipping import pako from "pako" var subscriptionHandler = createRelaySubscriptionHandler({ pusher: pusherClient, fetchOperation: fetchOperation, decompress: function(compressed) { // Decode base64 const data = btoa(compressed) // Decompress const payloadString = pako.inflate(data, { to: 'string' }) // Parse into an object return JSON.parse(payloadString); } }) Subscriptions with Ably require two things:
ably-js libraryfetchOperation function for sending the subscription operation to the serverPass ably: to get Subscription updates over Ably:
// Load the helper function import createRelaySubscriptionHandler from "graphql-ruby-client/subscriptions/createRelaySubscriptionHandler" // Load Ably and create a client const Ably = require("ably") const ablyClient = new Ably.Realtime({ key: "your-app-key" }) // create a fetchOperation, see below for more details function fetchOperation(operation, variables, cacheConfig) { return fetch(...) } // Create a Relay Modern-compatible handler var subscriptionHandler = createRelaySubscriptionHandler({ ably: ablyClient, fetchOperation: fetchOperation }) // Create a Relay Modern network with the handler var network = Network.create(fetchQuery, subscriptionHandler) With this configuration, subscription queries will be routed to ActionCable.
For example:
// Require the helper function import createRelaySubscriptionHandler from "graphql-ruby-client/subscriptions/createRelaySubscriptionHandler") // Optionally, load your OperationStoreClient var OperationStoreClient = require("./OperationStoreClient") // Create a Relay Modern-compatible handler var subscriptionHandler = createRelaySubscriptionHandler({ cable: createConsumer(...), operations: OperationStoreClient, }) // Create a Relay Modern network with the handler var network = Network.create(fetchQuery, subscriptionHandler) If you’re using Relay’s built-in persisted query support, you can pass clientName: to the handler in order to build IDs that work with the OperationStore. For example:
var subscriptionHandler = createRelaySubscriptionHandler({ cable: createConsumer(...), clientName: "web-frontend", // This should match the one you use for `sync` }) // Create a Relay Modern network with the handler var network = Network.create(fetchQuery, subscriptionHandler) Then, the ActionCable handler will use Relay’s provided operation IDs to interact with the OperationStore.
The fetchOperation function can be extracted from your fetchQuery function. Its signature is:
// Returns a promise from `fetch` function fetchOperation(operation, variables, cacheConfig) { return fetch(...) } operation, variables, and cacheConfig are the first three arguments to the fetchQuery function.fetch and return the result (a Promise of a Response).For example, Environment.js may look like:
// This function sends a GraphQL query to the server const fetchOperation = function(operation, variables, cacheConfig) { const bodyValues = { variables, operationName: operation.name, } const useStoredOperations = process.env.NODE_ENV === "production" if (useStoredOperations) { // In production, use the stored operation bodyValues.operationId = OperationStoreClient.getOperationId(operation.name) } else { // In development, use the query text bodyValues.query = operation.text } return fetch('http://localhost:3000/graphql', { method: 'POST', opts: { credentials: 'include', }, headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' }, body: JSON.stringify(bodyValues), }) } // `fetchQuery` uses `fetchOperation`, but returns a Promise of JSON const fetchQuery = (operation, variables, cacheConfig, uploadables) => { return fetchOperation(operation, variables, cacheConfig).then(response => { return response.json() }) } // Subscriptions uses the same `fetchOperation` function for initial subscription requests const subscriptionHandler = createRelaySubscriptionHandler({pusher: pusherClient, fetchOperation: fetchOperation}) // Combine them into a `Network` const network = Network.create(fetchQuery, subscriptionHandler) Since OperationStoreClient is in the fetchOperation function, it will apply to all GraphQL operations.