Industrial Resolution

View Original

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.

See this content in the original post

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.

See this content in the original post

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:

See this content in the original post

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.

See this content in the original post
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:

See this content in the original post

Let's briefly review the code above.

  1. 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.

  2. We create a very basic React component that simply displays a username and message in a <li> element.

  3. 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:

See this content in the original post
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:

See this content in the original post
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:

See this content in the original post

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:

See this content in the original post

This is all that is necessary to implement our subscription. Let's talk through a few of the specific parts:

  1. 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.

  2. 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.