Deploying Create-React-App with Rails API on Heroku

All we need is an easy explanation of the problem, so here it is.

I’m having an issue getting my react /rails app to work on heroku. I’ve managed to get it deployed and the rails server starts but I’m not seeing my react app. I feel like I’m close but can’t figure out what’s missing.

So my process is currently to run npm run build locally from the client directory which builds a ‘build’ directory in client. I then commit the results of the build and push to Heroku with git push heroku master.

I then navigate to the heroku app in a browser where I’m only getting a blank white page which is an index file the I manually copied from the build dir to public. I’m not sure if the index file is correct but I just wanted to make sure i could hit something.

Ultimately, I would like to just push the repo and it automatically run the build. I’ve seen this mentioned various places but I always get a react-script does not exist error when I run npm run build on the server.

My configuration is as follows:

basic structure of app

/app - root of Rails app
/client - root of React app
/public - does display index page

root/client/package.json

{
    "name": "qc_react",
    "version": "0.1.0",
    "private": true,
    "devDependencies": {
        "react-scripts": "^0.8.4"
    },
    "dependencies": {
        "react": "^15.4.1",
        "react-dom": "^15.4.1",
        "react-router": "^3.0.0"
    },
    "scripts": {
        "start": "react-scripts start",
        "build": "react-scripts build",
        "test": "react-scripts test --env=jsdom",
        "eject": "react-scripts eject"
    },
    "cacheDirectories": [
        "node_modules",
        "client/node_modules"
    ],
    "proxy": "${API_URL}:${PORT}/v1/"
}

root/package.json

{
  "name": "web",
  "version": "1.0.0",
  "description": "This repo contains a web application codebase. Read instructions on how to install.",
  "main": "index.js",
  "scripts": {
    "build": "cd client" # not sure what to put here but this gets me past build failure
  },
  "repository": {
    "type": "git",
    "url": "git+ssh://myrepo.git"
  },
  "author": "",
  "license": "ISC",
  "homepage": "https://myhomepage#readme"
}

Procfile

api: bundle exec puma -C config/puma.rb

Buildpacks

1. https://github.com/mars/create-react-app-buildpack.git
2. heroku/ruby

config/puma.rb

workers Integer(ENV['WEB_CONCURRENCY'] || 2)
threads_count = Integer(ENV['RAILS_MAX_THREADS'] || 5)
threads threads_count, threads_count

preload_app!

rackup      DefaultRackup
port        ENV['PORT']     || 3001
environment ENV['RACK_ENV'] || 'development'

on_worker_boot do
  # Worker specific setup for Rails 4.1+
  # See: https://devcenter.heroku.com/articles/deploying-rails-applications-with-the-puma-web-server#on-worker-boot
  ActiveRecord::Base.establish_connection
end

How to solve :

I know you bored from this bug, So we are here to help you! Take a deep breath and look at the explanation of your problem. We have many solutions to this problem, But we recommend you to use the first method because it is tested & true method that will 100% work for you.

Method 1

That’s because Rails is serving static files (including index.html) from /public. But your React app bundle is located inside /client/build. After build you need to copy these files back to Rails folder. The easiest way to do this is to add some scripts to your package.json:

"scripts": {
  ...
  "deploy": "cp -a build/. public",
  "heroku-postbuild": "npm run build && npm run deploy"
},

heroku-postbuild is executed automatically by Heroku after all dependencies are installed, just like normal postinstall hook.

Be careful with the paths for cp command. Your setup with 2 different package.json files is too complex. I recommend to use a single package.json inside root and set all paths accordingly.

Method 2

So I finally found the solution I was looking for. My code base has the following structure.

app/
client/
public/
...

I’m currently building my client solution as @yeasayer suggested above. After building and deploying my react project to the public folder in my rails api, I added the following to my routes:

... # api routes ...

get '/*path', to: 'react#index'

Then I created a react_controller and added the following contents:

class ReactController < ActionController::Base
  def index
      render :file => 'public/index.html', :layout => false
  end
end

Now any routes not caught by the api routes, will render the react app. I’m not sure why others don’t use this structure instead of using react_on_rails or some other plugin to achieve the same result. This setup is a lot simpler than dealing with these other solutions but I’d like to hear any thoughts on why this solution is not a good idea.

Method 3

I see you are using the create-react-app-buildpack, but I think your issue is that your react app is in a subdirectory. Buildpacks only get executed if heroku can detect a matching application in the directory and since your package.json in your root does not match create-react-app-buildpack I don’t think it is being used.

What you might try is removing the root package.json and using this sub directory buildpack so you can specify the locations of each buildpack directory

Note: Use and implement method 1 because this method fully tested our system.
Thank you 🙂

All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply