Set up your Rails and React apps for Heroku right from the start

23 Sep 2019 - John


If you’ve ever struggled to deploy your Rails and Ract apps to Heroku, this post is for you. Usually, the problems when deploying arise from the fact that despite we did extensive planning, we didn’t account for Heroku’s requirements, and now we’re trapped in a sea of logs and unmet dependencies. Also, Rails and React are sometimes built as separate apps, which requires two of the five app slots that Heroku offers for free accounts. Wouldn’t it be nice if we could deploy with no issues and just use one slot per app? Let’s do it!

Credit: Émille Perron @ Unsplash

To start with, start your rails app using postgresql right from the start. Also, create your react app in the same directory:

rails new . --api -d=postgresql && create-react-app frontend

This will create a Rails API with Postgres as the database and a React app under the /frontend sub directory.
Then you need to add the ./public folder to your .gitignore file. Then, if you’re on Mac or Linux, comment out the tzinfo-data gem line from your gemfile. You could leave it if you want to, but I find the constant warning annoying on Linux.
Now you need to setup your database:

rails db:create && rails db:migrate

and change ./config/puma.rb so that rails uses port 3001 instead of 3000:

port        ENV.fetch("PORT") { 3001 }

Then go to your ./frontend/src/index.js file and delete the serviceWorker import at the top and its respective call at the bottom. It should end up like this:

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
document.getElementById('root'));

Now go to your ./frontend/package.json file and add the following line in the top block:

"proxy": "http://localhost:3001"

This line is important. Since everything will run as one app, your rails API will run on port 3001 while your react app will run on port 3000. The line we just added means "route all requests to http://localhost:3001", which in turn means that your fetch requests will change. For example, if you’re sending a getrequest to http://localhost:3001/books/23, you’ll now have to send it to /books/23, since webpack is now taking care of the rest.
With that out of the way, now create two new files in your project root. One will be called Procfile and the other one will be called Procfile.dev. They’re used to tell Heroku what to run given the current environment. Your Procfile.dev should look like this:

web: PORT=3000 yarn --cwd frontend start
api: PORT=3001 bundle exec rails s

and your production Procfile should look like this:

web: bundle exec rails s
release: bin/rake db:migrate

Now go back to your terminal and in your root folder, type npm init -y to generate a minimal package.json file. Open it up and add the following to the scripts section:

"build": "yarn --cwd frontend install && yarn --cwd frontend build",
"deploy": "cp -a frontend/build/. public/",
"heroku-postbuild": "yarn build && yarn deploy"

Finally, create a file in ./lib/tasks called start.rake and add the following lines:

namespace :start do
  task :development do
    exec 'heroku local -f Procfile.dev'
  end
end
desc 'Start development server'
task :start => 'start:development'

Now assuming you have the Heroku CLI installed, all you need to do now is create a Heroku app:

heroku apps:create

And then tell it in what order it should use the buildpacks. We need to run the frontend first and then the backend, so you would need to enter the following commands:

heroku buildpacks:add heroku/nodejs --index 1
heroku buildpacks:add heroku/ruby --index 2

And that’s it. At this point, you can start writing your app like you’ve always done. To run it, you just need to type rake start and both React and Rails will start. Sweet, just one terminal window!
To deploy your app, you just need to git add ., git commit -m and then issue git push heroku master. Don’t forget to seed your database with heroku run rake db:seed!