Rails 5 API + Create React App: Full Stack Heaven

Just want the code? Go here: https://github.com/Nick-the-BinaryTree/BMS/tree/Part1

The release of API-only mode for Rails 5 apps offers a slimmed-down version of one of the most popular MVC frameworks. But what is it good for?

Before we answer that question, let’s jump over to the Javascript sphere. At the current point in time, this may feel a bit like standing on an exploding planet. However, for React developers, there is one major source of stability: Create React App.

You may remember this from your early days learning React as the suggested platform for completing the official tutorial. After that, if you’re like me, you decided it was time to take off the training wheels and start configuring Webpack yourself.

And then you found out what it was like to configure Webpack.

Roughly a month later, I had a firm new philosophy: Training wheels are absolutely lit.

Create React App gives us the best from the rapidly-expanding JS universe: hot reloading, pre-configured React, a dev server… the list goes on. Add some snazzy modules like material-ui to your package.json, and you’re free to be astonishingly productive.

After experiencing this 2017 front-end paradise, I was not about to give it up.

Enter left stage: Rails 5 API-Mode

Imagine for a moment, your back-end development could retain all the comfort and agility of writing Rails models, controllers, and migration-based database management, while simultaneously your front-end development could take advantage of the latest and greatest tools from the JS community.

Let’s do that.


I’m going to assume you have Rails 5 and Create React App installed.

First, let’s create a project. Open a terminal, and…

rails new BMS --api

What is a BMS, you ask? A Banana Management System, of course. And we’re going to code one. (As you can tell, I am an unusually straitlaced and mature adult).

cd BMS

We’re going to start with the front-end, so now that you’re in your project root directory, type:

create-react-app client

Epic. To make sure everything works on the front-end:

cd client

npm start

Spinning symbol of React amazingness? We’re off to a good start.

Control-C to shut down the dev server, and, in your favorite code editor, open the client folder. This isn’t a React tutorial, so I won’t go too deep into this code, but let’s make our App.js file look like this:

import React, { Component } from 'react'
import './App.css'
class App extends Component {
constructor(props) {
super(props)
this.state = {bananasReceived: ""}
this.getBananas = this.getBananas.bind(this)
}
getBananas() {
this.setState({bananasReceived: "Where are my bananas?"})
}
render() {
return (
<div className="App">
<button
onClick={this.getBananas}
style={{marginTop: '25vh'}}
>
Get Bananas
</button>
<p>{this.state.bananasReceived}</p>
</div>
);
}
}
export default App

And run npm start.

Wow. Really brilliant piece of software already. If members of the banana industry aren’t currently blowing up your inbox, let’s continue.

In your terminal, ctrl-c out of the server, and cd ../

Now let’s create all the necessary files for our banana model:

rails generate scaffold banana name:string location:string

I’m going to assume you already have some Rails knowledge, and I don’t have to explain what happened there. However, I will note that if you want to be fancy, you could also have typed:

rails g scaffold banana name location

Go ahead, and migrate your database to prepare it for the bananas:

rails db:migrate

Great, now let’s get in there, and add some fruit.

rails console, or rails c (for the fancy pants).

Now type:

Banana.create(name: "Tim", location: "Croatia")

Banana.create(name: "Roderick", location: "Chile")

(Did I mention that you’re an international banana merchant?)

Type ctrl-d to exit the Rails console.

So how do we reach these bananas?

Well, we’re making a Rails API, so I suppose we should put all the banana URLs behind an “api” namespace. Change your config/routes.rb file to look like this:

Rails.application.routes.draw do
scope '/api' do
resources :bananas
end
end

Awesome! Now let’s head back to the front-end: cd client

We’re going to setup Create React App’s built-in proxy. And yes, the fact that it has a built-in proxy is another reason to love Create React App. In the package.json file, add the following after scripts:

"proxy": {
"/api": {
"target": "http://localhost:3001"
}
}

A few things here:

  1. If you get errors, make sure you have a comma after the scripts bracket.
  2. There is shorter syntax for this proxy, but I like this long form version because it allows you to go to API addresses directly in your browser. Also, it becomes easy to tack on other URLs to proxy (think /admin).
  3. Yes, you guessed it. We’re going to run our Rails server on port 3001.

Make sure you’re in the client folder, and npm start

Now, in your browser, go to localhost:3000/api/bananas

…and nothing.

Oh yeah. We forgot to start our Rails server. Open a new terminal (ctr-shift-t on some systems), navigate to the root of the Rails project, and run:

rails server -p 3001

Okay, we should be fine now. Visit localhost:3000/api/bananas

It’s our banana JSON!

Take a moment to pat yourself on the back. Bridging the gap between front-end and back-end is no simple task.

Let’s make that “Get Bananas” button functional.

We’re going to use jQuery for API calls, but you can try something else like fetch. Make sure you’re in the client directory, and run npm install --save jquery

Your package.json file should now look something like this:

{
"name": "client",
"version": "0.1.0",
"private": true,
"dependencies": {
"jquery": "^3.2.1",
"react": "^15.6.1",
"react-dom": "^15.6.1"
},
"devDependencies": {
"react-scripts": "1.0.10"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
},
"proxy": {
"/api": {
"target": "http://localhost:3001"
}
}
}

Go ahead, and change your src/App.js file to this:

import React, { Component } from 'react'
import './App.css'
// $ is a shortcut for jQuery methods
import $ from 'jquery'
class App extends Component {
constructor(props) {
super(props)
this.state = {bananasReceived: ""}
this.getBananas = this.getBananas.bind(this)
}
getBananas() {
$.ajax({
url: "http://localhost:3000/api/bananas",
type: "GET",
context: this, // Allows us to use this.setState inside success
success: function (result) {
this.setState({bananasReceived: JSON.stringify(result)})
}
})
}
render() {
return (
<div className="App">
<button
onClick={this.getBananas}
style={{marginTop: '25vh'}}
>
Get Bananas
</button>
<p>{this.state.bananasReceived}</p>
</div>
);
}
}
export default App

Fantastic. Banana JSON straight to your screen.

Okay, so now we know how to make a button click send API requests. Let’s learn one more thing. Is it a little pesky to start two servers? We can fix that.

In your Rails project root directory, edit your Gemfile to add gem 'foreman'. A nice spot is right above group :development, :test

Now run bundle install, or bundle for short, from the root directory.

Create a Procfile in your root directory with no file extension.

Fill it with these two lines:

web: cd client && PORT=3000 npm start
api: rails server -p 3001

Can you guess what will happen when we run foreman start?

Pretty nifty, right?

Well, now you’re all set to develop a fully-featured suite of banana software (or other, less important projects).


In conclusion, decoupling the front-end and back-end allows for simultaneously using the best tools of each sphere — a powerful method for development.

If you’re fortunate enough to have dedicated front-end and back-end teams, the effect is multiplied. Front-end developers can stop worrying about the back-end beyond a few API endpoints and focus on staying on top of the booming JS world.

Back-end developers can do their job with whatever framework and database they want: Laravel, Django, MySQL, MongoDB… All they have to do is ensure the API endpoints are ready to serve. Heck, an entire mobile version of the app could be developed, and they wouldn’t have to lift a finger; it would just use the same API.

But despite the vast options (and modern web-developers know I truly mean vast), Rails 5 API Mode and Create React App are great tools for the task.

Next time, I’ll show you how to make this stack even stronger with JWT authentication.