Relay/GraphQL On Rails

Image for post
Image for post

Summary: If you are excited about just declaring data required by your UI components while leaving the difficult parts of data fetching, updating, and data handling performance issues to a framework, you will be excited by Facebook’s recent release of Relay/RelayJS (Sep 2015), which works with React UI (Oct 2014). This article shows you how to to integrate Relay/React into Rails.

NOTE:

  • You can clone this starter-kit from Github, and start using Relay immediately without reading further
  • Conversely, with instructions here you can re-create the starter-kit on Github

Facebook introduced React (Oct 2014) to make creating rich web UI easy to reason, and debug with its uni-directional data flow; all data required by the entire web UI on a page is piped through a top-level UI component, which percolates only the necessary data to each sub-components under it.

However, the React component (usually the top-level component), or the optional Flux framework (if you implemented it), is still responsible for fetching the required data using Ajax, etc., and you have to write that.

Enter Relay! When you create a React component, you just need to declare the data that it needs, and Relay will handle the gory details of fetching the data efficiently, which includes batching queries, collapsing access patterns, caching, etc.

Overview

Assumptions:

  • You have Rails installed (tested on Rails 4.2 only)
  • You have >= ruby-2.1

Goals:

  • Make it easy to understand (see Relay in Pictures section) Relay
  • Setup Relay/React on Rails so that we can create a very simple Rails/Relay/React application, which outputs to the browser:

NOTE: The 2nd line ‘Boarding… Simpleton’ is a React component, which relies on Relay to pull its data (Simpleton) from the server over the Internet.

  • Use this setup as a foundation to further experiment with Relay

Why this is difficult:

As of this writing (15 Sep 2015), I could not find any information on getting Facebook’s Github Relay or npm-based ‘react-relay’ to work with Rails.

The existing Facebook tutorial:

  • Does not show clear distinction between client-side, and server-side library requirements because in its case, both use Javascript libraries. Specifically, it does not address the question — “which libraries are required to make a non-Javascript-based server Relay-capable, and which libraries should be delivered to the browser to make the client-side Relay-capable?
  • Has no information on how to transpile Relay/GraphQL client-side Javascript code, which may reside on a non-Javascript-based server, but needs to be delivered to browser for execution
  • Is not Rails-based so you need to hunt down nuggets of information from all over the web

Relay In Pictures

Relay consists of 3 parts:

  1. The schema for our data specified in GraphQL
  2. A GraphQL server, which can load up the schema, and respond to client queries in GraphQL
  3. A ‘transport’ layer through which a client communicates with the GraphQL server via GraphQL language

For our purpose, architecturally, this translates to:

  • A Rails server acting as a GraphQL server serving our data based on our schema (requirement 1. & 2.)
  • The Rails server delivering the necessary client-side Javascripts required for the browser to communicate with the GraphQL server through the Relay ‘transport’ layer, using the pre-agreed data schema (requires 1. & 3.)

To facilitate understanding, we illustrate the Relay nodejs setup used by the Facebook tutorial, and then compare what that setup should look like for Rails.

Image for post
Image for post
nodejs Relay Setup
Image for post
Image for post
Rails Relay Setup

Things of note:

  • Our GraphQL data schema (bottom-left box) is in either Javascript (nodejs server) or Ruby (Rails sever) but is converted using a npm script (nodejs) / Rake task (Rails) to JSON (follow the arrow upwards from the bottom-left box); nodesjs/Rails server-side code can use the GraphQL schema in Javascript/Ruby format, but babel-relay-plugin (more on this in next) needs it in JSON format
  • Transpilation of Relay.QL code (top-left box) to Javascript is done by babel (nodejs) / babelify (Rails) with the help of the babel-relay-plugin based on GraphQL data schema (JSON format)
  • We can clearly see what libraries are needed to make nodejs/Rails speak GraphQL, enabling it to host a /graphql endpoint (bottom-right box); the arrows from libraries/Gems pointing to the ‘Pure JS server-side /graphql endpoint’ (bottom-right box)
  • All Javascript code that needs to be delivered to the client-side (top-right box) is bundled using webpack (nodejs) / browerify-rails (Rails), which is also where transpilation is configured

Let us look at how we can setup each component in detail next.

Rails GraphQL Server

GraphQL-speaking Rails

Add the following gems and run ‘bundle install’:

graphql-ruby bestows Ruby with the ability to understand GraphQL, while graphql-relay-ruby provides helpers to handle Relay specific-concepts such as:

  • Associating a piece of data with a global id to facilitate re-fetching
  • Tying a piece of data with associated data, e.g., a piece of data representing a Person, can have many associated pieces of data about each of his Friend
  • Updating a piece of data with input from client (Relay uses the fancy term ‘mutation’)

GraphQL Data Schema

Facebook’s official tutorial shows how we can use GraphQL for data schema definition. Unlike the tutorial, instead of defining the data schema in Javascript (data/schema.js), we define it using graphql-ruby classes in a set of files under app/graph.

We will present a very simple GraphQL schema here as it is a huge topic, and Facebook’s tutorial has covered it quite a bit.

A GraphQL schema consists of a Query, and Mutation type, for handling queries and updates respectively. Each of these is compose-able out of other GraphQL types, e.g., our Simple query is composed from SimpleType (see below).

The first thing that you need to implement for the schema is the standard NodeIdentification interface; each piece of data that a client fetches through Relay/GraphQL comes from an instance object, e.g., an ActiveRecord, in Rails. Each of them is also assigned a global ID, that when passed back to Relay/GraphQL, enables Relay/GraphQL to re-fetch that same instance object (whose data may have changed). For more details read this, but in the same vein as Facebook’s Relay tutorial, this was introduced at the start.

Next, let us look at the GraphQL schema Query (but leave out Mutation as an exercise).

This schema enables us to query data by either of the fields:

  • :simple with some arguments (args)
  • :node id

Under the :simple field:

  • resolve tells us how to get the data. In this case, we merely return a constant hash. However, if arguments were provided in the GraphQL query, e.g., a user email, we can access it in args and fetch data based on it by replacing resolve with:
  • SimpleType defines the structure of the data we fetch (app/graph/types/simple_type.rb)

We bind the Query type into our data schema in app/graph/relay_on_rails.rb. This is also where you bind a Mutation type if you have one.

To ensure that Rails can find our schema in app/graph/types, add to config/application.rb:

At this stage you can test that Rails can speak GraphQL through the RelayOnRailsSchema object, by firing up your Rails console:

GraphQL Endpoint

With Rails speaking GraphQL, and understanding our data schema, all we need now is to expose the GraphQL endpoint /graphql that the client-side Relay ‘transport’ communicates to by default.

Define the /graphql endpoint in config/routes.rb:

Handle the /graphql endpoint with the GraphqlController#create method in app/controllers/graphql_controller.rb.

Start the rails server using within the root directory.

Now test the endpoint from the command line:

The Rails GraphQL server is now ready.

Serving Client-side Relay Javascripts

Relay/React Javascript Libraries

From the Rails Relay Setup illustration, we know that we need to deliver Relay/React Javascript libraries (react, react-relay, etc.) to the client-side. We prepare them on the server-side first with npm which relies on a package.json file that specifies all the Javsacript libraries:

Do:

Which downloads all the libraries into node_modules/. To deliver them to client-side, we use browserify-rails, which bundles node_modules/ content into Rails assets pipeline without requiring any configuration.

Add the gem and run ‘bundle install’:

Relay.QL and GraphQL Data Schema

With Relay, for each React component in our client-side Javascripts, we create a RelayContainer counterpart to declare its data requirement:

In the RelayContainer, there are Relay.QL fragments, which are NOT Javascripts, and needs to be transpiled based on the GraphQL data schema we defined at our GraphQL server.

Facebook provides the babel-relay-plugin to do the required transpilation, but it requires our data schema in JSON format as input. To convert our Ruby-based GraphQL schema in app/graph into a JSON file (app/assets/javascripts/relay/data/schema.json), use the Rails console:

If you are using the starter-kit, just do:

Create a new transpilation plugin by initializing babel-relay-plugin with the JSON schema file as shown in app/assets/javascripts/relay/utils/babelRelayPlugin.js:

Transpile Relay.QL fragments

Configure browserify-rails to perform transpilation (while doing bundling) in config/application.rb:

Note that the plugin we created from babel-relay-plugin, relies on babelify.

NOTE: As bonus, babelify can also transpile JSX tags if they are used in our React components. However, babelify seems to do JSX transpilation only if your files have .jsx extension.

Displaying the Relay/React App

Prepare a Javascript snippet to render the RelayContainer (BoardRelayContainer) in app/assets/javascripts/relay/boardapp.react.jsx:

Make the BoardRelayStart function available. The simplest way is declaring it in app/assets/javascripts/application.js:

Lastly, deliver the UI to the user through a HTML page with a Javascript to call BoardRelayStart function in app/view/static_pages/board.html:

The controller for this page is in app/controllers/static_pages_controller.rb:

And its routes in config/routes.rb:

I hope you will find this Relay On Rails starter-kit useful! Let me know of any errors, suggestions, etc. @neth_6.

Resources

Facebook Relay: The source of authoritative information on Relay

Facebook Relay Tutorial: I read this (> 2 times start-to-end) to get started

Facebook Relay Github Repo: The examples provide useful references for various GraphQL schema definitions, and client-side Relay Javascript and Relay.QL snippets

Facebook Relay Starter Kit: If you are doing everything in Javascript, and have a preference to use webpack for Javascript bundling/transpilation, clone this to get started, or refer to it for webpack configuration

Simple Relay Starter Kit: If you are doing everything in Javascript, and have a preference to use browserify for Javascript bundling/transpilation, clone this to get started, or refer to it for brwoserify configuration

graphql-ruby & graphql-relay-ruby: The authoritative sources for defining GraphQL schema in Ruby GraphQL classes, and getting Rails to speak GraphQL.

browserify documentation: Command-line options required to do transpilation, e.g., —plugin, etc.

React Weekly

A curation of the best React JS related content —…

Soon Hin Khor, Ph.D.

Written by

Use tech to make the world more caring, and responsible. Nat. Univ. Singapore, Carnegie Mellon Univ, Univ. of Tokyo. IBM, 500Startups & Y-Combinator companies

React Weekly

A curation of the best React JS related content — http://www.reactweekly.co

Soon Hin Khor, Ph.D.

Written by

Use tech to make the world more caring, and responsible. Nat. Univ. Singapore, Carnegie Mellon Univ, Univ. of Tokyo. IBM, 500Startups & Y-Combinator companies

React Weekly

A curation of the best React JS related content — http://www.reactweekly.co

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store