Skip to main content

Real-Time Data

If we want to keep our data up to date with the latest version from the server, the first thing to consider is if it appropriate to use any real-time features, which can make it easier to automatically keep the data up to date without manually refreshing (aka “refreshing a query”) the data periodically.

GraphQL Subscriptions are a mechanism which allow clients to subscribe to changes in a piece of data from the server, and get notified whenever that data changes.


Writing Subscriptions

A GraphQL Subscription looks very similar to a query, with the exception that it uses the subscription keyword:

subscription DashboardTickerSubscription($symbols: [String!]) {
onPriceChange(symbols: $symbols) {
...DashboardTickerItemFragment_assetprice
}
}

In order to execute a subscription against the server in Relay, we can use the requestSubscription and useSubscription APIs. Let’s take a look at an example using the useSubscription hook:

@/scenes/dashboard/DashboardTicker.js
import {memo, useMemo} from 'react';
import {graphql, useFragment, useSubscription} from 'react-relay';

import {Ticker} from '@/components';

import DashboardTickerItem from './DashboardTickerItem';

export default memo(function DashboardTicker({fragmentRef}) {
const data = useFragment(
graphql`
fragment DashboardTickerFragment_query on Query {
ticker: assets(
first: 10
order: {price: {tradableMarketCapRank: ASC}}
) {
nodes {
id
symbol
...DashboardTickerItemFragment_asset
}
}
}
`,
fragmentRef,
);
const assets = data.ticker?.nodes;
const symbols = assets?.map(({symbol}) => symbol) ?? [];

useSubscription(
useMemo(
() => ({
subscription: graphql`
subscription DashboardTickerSubscription($symbols: [String!]) {
onPriceChange(symbols: $symbols) {
...DashboardTickerItemFragment_assetprice
}
}
`,
variables: {symbols},
}),
[String(symbols)],
),
);

return (
<Ticker>
{assets?.map((asset) => (
<DashboardTickerItem key={asset.id} fragmentRef={asset} />
))}
</Ticker>
);
});

We pass into the hook the following arguments:

Behavior, it’ll:

  • Subscribe when the component is mounted with the given config.
  • Unsubscribe when the component is unmounted.
  • Unsubscribe and resubscribe with new values if the environment, config or requestSubscriptionFn changes.

Configuring the Network Layer

You’ll need to Configure your Network layer to handle subscriptions.

Usually GraphQL subscriptions are communicated using SSE or WebSockets.

Here’s an example using graphql-sse:

import {Network, Observable} from 'relay-runtime';
import {createClient} from 'graphql-sse';

// ...

const sseClient = createClient({
url: 'http://localhost:5000/graphql',
});

const subscribeFn = (operation, variables) =>
Observable.create((sink) =>
sseClient.subscribe(
{
operationName: operation.name,
query: operation.text,
variables,
},
sink,
),
);

const network = Network.create(fetchFn, subscribeFn);

Here’s an example using graphql-ws:

import {Network, Observable} from 'relay-runtime';
import {createClient} from 'graphql-ws';

// ...

const wsClient = createClient({
url: 'ws://localhost:5000/graphql',
});

const subscribeFn = (operation, variables) =>
Observable.create((sink) =>
wsClient.subscribe(
{
operationName: operation.name,
query: operation.text,
variables,
},
sink,
),
);

const network = Network.create(fetchFn, subscribeFn);