GraphQL + Relay Modern + Rails

The path of least resistance

Countryside by Pexels is licensed under Creative Commons CC0

So, you’re a Rails dev who knows some JavaScript and who wants to do GraphQL + React + Relay (Modern) on top of your favorite web framework. Integration with modern front-end technologies isn’t Rails’ strong suit right now, hence the proliferation of approaches out there for bringing up FaceBook’s JavaScript framework on top of the trusty Rails we all know and love.

Taking the next step and adding Relay and GraphQL into this equation further alters the effort/reward trade off equation of doing React on Rails, so if you head down this road then it’s worth thinking very carefully about your relationship to Rails and whether or not you really need to fight the Rails front-end battle to make this all work.

I’ve been hacking away at this particular set of problems on and off for about a year now, and I’ve finally come up with a solution that I’m more or less happy with. I use Rails, and I also don’t use Rails.

Confused? Here’s the longer explanation I made this all work, laid out in a good news/bad news format. First, the bad news, which comes in multiple parts. (Brace yourself, because it’s pretty bad, but don’t stop reading because the good news is pretty good.)

The Bad News: You Have to Do All the JavaScript, First

In order to really do GraphQL/Relay/Rails (I’m going to refer to this as “the GRR stack,” as an oblique homage to everyone’s favorite super awesome, but perennially delayed fiction writer), you’ll first to have to learn how to do everything in JavaScript using Node.js for the back-end. There is really no other way around this.

As you’re trying to grok all of the fundamental concepts involved in getting this stack working, all of the code in the tutorials and docs you come across will be written in some flavor of JavaScript. Thankfully, most of them are in ES6 nowadays, which helps if you’re coming into this without a strong JS background (raises hand), but you’ll still need to get fairly comfortable with the ins and outs of modern front-end programming. This will take some time, because the modern front end is a hot mess.

You’ll also want to learn Node.js and express on the server side, because, again, that’s what the tutorials and docs are in. But the requirement that you learn how to do everything in Node before doing it in Ruby/Rails isn’t just so that you can read the (ever-changing, and often subtly out-of-date) tutorials and documentation. It’s also because the following is where you’re going to end up when you finally get around to doing rails new my_react_app and working with the excellent graphql-ruby gem: “I know how this is working in the JS example app I cloned, so now I just need to figure out how to do the exact same thing in Ruby.”

Again, the React and larger Node.js ecosystem is in constant, stomach-churning flux, so cloning into a JavaScript demo app and looking at the source in order to figure out how to translate a bit of critical functionality into Ruby/Rails isn’t some initial learning phase that you’ll go through – it’s your life for as long as you’re going down this road.

This brings me to the second part of the bad news, and it’s one that will cause some of you to close the browser and quit reading right now: that there is no point in trying to fight the legacy Rails front-end machinery (especially 5.1’s horribly broken native webpack integration) in order to make all of this work, so you might as well just use Nodejs for a front-end server.

By the time you’ve gone through all of the same full-stack JavaScript tutorials as the dedicated JavaScript devs, and gotten comfortable with things like webpack and ES6 and Express and the rest of the modern JavaScript menagerie, and truly comprehended the full implications of the fact that a GraphQL server has only one endpoint and is as different from REST as swimming is from walking, then you’ll ultimately come around to the realization that using Node is just easier – it really is.

At this point, you face a fork in the road: you can choose to ditch Rails entirely and just stay in Node.js-land for the whole app, or you can resign yourself to the fact that your actual front-end web server will be Node + Express, while your Rails server will run in API mode and serve up that lone /graphql endpoint.

I’ve chosen the latter path, and after some time on it I can say that so far it’s working out pretty well. I’ve not shipped anything to production with this arrangement yet, but I’m enjoying the process of building the standard Rails blog app this way. This brings me to the good news.

The Good News: Best of Both Worlds

The good news about this dual-server arrangement is that you really do get the best of both worlds: on the front-end, you can use all of the new new hotness – not just React, but CSS modules, tools like Storybook, and all the other awesome things that JS types take for granted nowadays and that you’ve starred on GitHub but despaired of ever getting to try in real-life because Rails.

Not only can you use this stuff, but you can use it exactly like it’s used in the demo apps you’re learning from, without a bunch of painful, Rails-specific tweaks to get it sorta working.

On the back-end, where all of your business logic resides, and where you talk to the database, and to various external APIs, you get to use Rails! This is huge, because despite what you read on Hacker News about how Golang makes your whites whiter, Scala wards off ill-health and bad luck, and Elixir reverses male-pattern baldness, Rails is still super duper mega-awesome for all things back-end.

Indeed, as a Rubyist fumbling through the Node.js ecosystem for the first time, you’ll often come across things that will shock and horrify you. “OMG people actually live this way?!?” is a common reaction, although the situation is improving rapidly as the number of experienced developers working in this space grows.

The biggest deficiency, and one that doesn’t seem to be improving nearly as fast as JS itself, is the lack of emphasis on testing. Most demo apps you come across will not have tests of any kind at all in the repo. This is frustrating for a Rubyist as most of us examine the test suite in order to figure out how a new piece of software actually works.

As you experiment with various boilerplates and demo apps, you’ll have the urge to write tests using something like Jest. Jest is quite good, and is explicitly modeled on RSpec so it’s familiar to Rails devs. But the problem isn’t so much Jest as it is general testing and debugging in Node.js.

If you’re writing your Node.js code using ES6 modules (i.e. import instead of require), then you’ll have to run your code through Babel in order to make it work. This means you’ll be debugging translated code, usually without working sourcemaps, which is not awesome. So probably don’t do that.

I also found getting breakpoint debugging working the way I expected to be a huge pain. I’ll probably write a bit about this, if I can remember the exact formula I used to get it all working.

Then there are all the other non-helpful things about JS, like the error message (the famous undefined is not a function everywhere), the eternal callbacks, and so on.

Finally, as you debug, you’ll be picking your way through unfamiliar stack traces, which will slow you down more than you might think. You don’t realize how fast you are at reading a Rails stack trace in order to pinpoint a problem until you set about untangling a Node + Express stack trace for the first time.

Sure, if you’re willing to invest the time in relearning everything you know about how to test and debug an app, then maybe you’ll eventually, in the very long run, get back to being almost as fast as you were with Rails. But why do that, when you can just go back to Rails?

So that’s where I am with this, as of right now. Rails in API mode for serving up GraphQL, and Node.js with Express for serving up the front end, both in the same repo. We’ll jump into that repo next time.

Finally, Some Code

I don’t want to wrap up this first installment without showing you at least a bit of code, so take a look at the snippet below:

app.use('/graphql', (req, res) => {
  req.pipe(request(`http://localhost:${RAILS_PORT}/graphql`)).pipe(res)
})

This is the code that I use to tell the Express front-end server to forward all of the traffic to /graphql to my back-end Rails/GraphQL server. That’s it – all of it. I didn’t have to look at a NIX config file for a reverse proxy, or do anything fancy to make Express pass all the requests (including cookies, headers, the works) transparently to my Rails GraphQL server. The above single-line bit of Express middelware code Just Works.

When I set about creating the rails_relay_authentication repo that we’ll dig into next time, my goal was to take the entire /server directory of the original relay-authentication example app and seamlessly swap it out with Rails.

I was able to do this iteratively by using the port option in the above line of code to point the front end at the original node server for a reference, then at my Rails back end to compare GraphQL responses to the same Relay queries.

If you want to clone my repo and give it a try, you can do this yourself in order to easily switch back and forth between the GraphQL servers and poke at them each in turn with a GraphiQL client.

Next Steps: Getting Rid of Node.js, Too

Here’s where I want to talk all of this, eventually: I’d like to ditch Node.js/Express entirely and use Netlify to serve the client directly off of a CDN, with no front-end server.

In fact, I don’t actually have a real plan to deploy the above as-is. Instead, I want to use Node.js + Webpack as a static site generation tool, and push the client build up to Netlify so that there’s no front-end server at all, just JavaScript that you download from a CDN and use to connect to my Rails GraphQL server.

I’m not there, yet, but I’m looking into it and will publish another piece if I can get it working. If you’ve done this or have any tips for me, please drop them in the comments, below.

Photo of Jon Stokes

Jon is a founder of Ars Technica, and a former Wired editor. When he’s not developing code for Collective Idea clients, he still keeps his foot in the content world via freelancing and the occasional op-ed.

Comments

  1. Matt
    August 04, 2017 at 6:19 AM

    I’m really confused by the title….