USING SUBSCRIPTIONS TO UPDATE DATA IN RELAY MODERN
Built With
This post was written using:
React 16.8.x
Relay 4.0.x
GraphQL Spec - June 2018
NodeJS 8.x LTS
Overview
While working on a Relay project, I ran across a common application pattern that can be hard to understand if you're new to GraphQL and Relay: using GraphQL Subscriptions to update a client that already has data from an initial GraphQL Query. Imagine you're building a simple chatroom or collaborative Todo List, for example, that initially fetches data from a server then uses websockets to keep the React UI up to date in real time.
Since Relay is a fairly new and evolving framework finding good examples of subscription patterns was difficult. Figuring out how to implement this pattern for the first time took me several hours of combing through documentation and trial and error, so I hope to save others this headache by providing a straightforward example.
If you follow Relay's requirements for GraphQL data structures, subscriptions can easily be used to supplement existing GraphQL queries. As new data is received it can be inserted into the Relay store, allowing your UI to update based on the new data without any rendering awareness of subscriptions.
This post will walk you through building a basic React component that mocks a simple chat interface using Relay Modern, fetching initial data with a GraphQL Query, then receiving updates using a GraphQL Subscription. You'll start with a vanilla create-react-app application and install Relay and other necessary tools along the way.
For an introduction to GraphQL, check out HowToGraphQL and for an intro to the Relay framework, see Introduction to Relay. Relay is a performance-oriented framework that couples React with GraphQL.
1. Prerequisites
First we need a GraphQL server to connect our Relay app to, and more importantly, we need the server's schema so that Relay understands the data the server can provide.
A GraphQL Server
The details of setting up a GraphQL server are beyond the scope of this tutorial, but a simple server is provided for demo purposes. Clone the server from https://gitlab.com/brewcore/graphql-relay-subscription-demo-server and run npm install
followed by npm start
to spin up the server for this demo.
Take a look at the GraphQL schema below to see what the server offers, and open the graphql explorer at http://localhost:4001/graphql to experiment with the server.
This schema has two important parts: a Query that allows the client to fetch a paginated list of Messages, and a Subscription that allows the client to receive new Messages in the same format as the original paginated list. The schema follows Relay's GraphQL specification, implementing nodes, cursors, and edges to standardize our data for Relay.
Creating an app with create-react-app
Next, create a new React application using create-react-app, a helpful tool for creating React applications.
2. Setting up Relay
Your next step is to integrate Relay with React and configure the Relay environment, so Relay knows how to talk to the GraphQL API.
Install Relay Dependencies
First you need to install some dependencies for Relay. Run the following commands:
Relay requires the use of a compiler to convert graphql
statements to optimized runtime code. For more info about the Relay compiler see https://relay.dev/docs/en/graphql-in-relay.html#relay-compiler.
Configure a Relay Environment
A Relay environment brings together everything Relay needs to function, which is at a minimum a Store which handles data storage and caching, and a Network Layer which handles communications with the GraphQL server.
Create a new file at src/Relay/Environment.js
and add the following code. This is a basic Relay Environment that can handle GraphQL Queries - you'll add subscription support later. For more info on Relay environments see https://relay.dev/docs/en/relay-environment.html.
export default environment
3. Building React Components and Fetching Initial Data
Now lets set up React components to display our messages. We know from looking at our server's schema that messages will have id, username, and message fields. Create a file at src/Message.js
and add the following code to it:
Let's briefly review the code above.
We import React and utilities for Relay. Note that
graphql
is imported from a babel plugin - this is what allows the Relay compiler to work its magic on GraphQL statements during compilation.We create a very basic React component that simply displays a username and message in a
<li>
element.We use a Relay Fragment Container to specify the data our component needs.
Now open src/App.js
and replace its code with the following:
export default App
In this component we're implementing Relay's QueryRenderer to fetch up to the last ten messages on the server and render them in a list using our simple Message component.
Note the @connection(key: "AppQuery_messages")
annotation. This annotation is an identifier that will help us later when we want to display additional messages from a subscription - think of it as a tag that makes it easy to find a collection of GraphQL records in the Relay store.
This completes the basic React app displaying data with a GraphQL Query. Let's build the app and see it in action!
Run npm run relay
to execute the Relay compiler, then run npm start
to launch the app in a browser.
Important Note: the Relay compiler must be run any time you modify your GraphQL statements in your React app.
Now let's add new messages to our list by implementing a GraphQL subscription.
4. Implementing Subscriptions
If you look at the GraphQL schema you can see that the server offers one possible subscription, called 'messages', and it returns data of the same type as our earlier GraphQL query - a newMessageEdge
. Let's implement a subscription to subscribe the client to any new messages sent by the server (the demo server automatically makes new messages every few seconds).
Install Additional Dependencies
First we need to install some additional dependencies that will help us connect Relay to our GraphQL server's websockets.
npm install --save apollo-link apollo-link-ws subscriptions-transport-ws
Update Relay Environment
Open src/Relay/Environment.js
and import those dependencies at the top of the file:
export default environment
You can see the full updated file here. This sets up a new websocket connection to the server and tells Relay's network layer how to use it.
Update App.js
Finally, we need to update src/App.js
to set up our subscription when the component mounts, and remove it when the component unmounts.
Add the following import to src/App.js
:
ConnectionHandler is the magic sauce that allows us to connect our newly received subscription data to existing data in the Relay store.
Add the following functions just after class App extends Component {
in src/App.js
:
This is all that is necessary to implement our subscription. Let's talk through a few of the specific parts:
subscription - This is our GraphQL subscription statement - note that we're still using Relay's composition here to determine which properties of a message we care about, using
...Message_message
.updater - This function tells Relay how to update the store with the new data received from the subscription.
First we get our newly received record.
A. Next, we get the 'connection' used by our original GraphQL Query. The second argument to
getConnection()
is the key we previously set in the annotation in our QueryRenderer component.B. We now tell Relay that this new data is an edge, and can be connected / inserted after the edges we got from our original query.
Let's re-compile our Relay app and start it to see subscriptions working.
npm run relay
npm start
Summary
The steps to making a Relay framework based React component subscription-aware are: configuring the network layer, setting up a subscription handler, tagging the connection where new data will be inserted in the store, and telling the store how to connect the new data to existing data using ConnectionHandler
from relay-runtime
. I hope this saves someone a few hours of digging through documentation.