React + RailsのアプリをHerokuで動かす方法

# はじめに

フロントエンド用アプリとしてReact(Create React App)、バックエンド用アプリとしてRails(API mode)を使用し、Herokuで動かすまでの手順をまとめてみました。

Herokuは無料プランでフロントエンド、バックエンドの2つのアプリを立ち上げます。

まとめた内容としては以下の流れとなります。

1. ローカルで動くものを作る
1. Herokuにデプロイする
1. Herokuで動かす

# 環境

|       | バージョン |
| :---- | :-------- |
| node  | v11.3.0   |
| npm   | 6.4.1     |
| React | 16.4.2    |
| Rails | 5.2.1     |
| Ruby  | 2.5.1     |

## 事前準備

1. Herokuのアカウント登録
2. ローカルで `create-react-app` インストール（本記事に沿って `react` プロジェクトを作成する場合は必要です。）

# 1. ローカルで動くものを作る

まずはローカルで動くものを作ってみます。

以下のフォルダ構成を作成します。

```
react-rails-heroku
 └ projects
   ├ react-frontend
   └ rails-backend
```

`react-rails-heroku` にはローカルで起動する際、フロントエンドとバックエンドをまとめて起動できるようなコマンドを仕込みますが、後ほど説明します。

```
$ mkdir react-rails-heroku && cd react-rails-heroku
$ mkdir projects && cd projects
$ create-react-app react-frontend
$ bundle init
# 生成されたGemfileにある `gem "rails"` のコメントアウトを外す
$ bundle install --path vendor/bundle
$ bundle exec rails new rails-backend --database=postgresql --skip-bundle --api
```

これで一旦プロジェクトの雛形がそれぞれ出来ました。

## 1.1. バックエンドの実装

フロントエンドとのやり取りを行う為のエンドポイントをバックエンド側に用意します。

```:/react-rails-heroku/projects/rails-backend
# フロントエンドからのアクセスを許可する為に `gem 'rack-cors'` のコメントアウトを外す
$ bundle install --path vendor/bundle
$ bundle exec rails db:create
```

ルートを追加

```ruby:config/routes.rb
Rails.application.routes.draw do
  get "hello_world", to: 'application#hello_world'
end
```

`hello_world` メソッド定義

```ruby:app/controllers/application_controller.rb
class ApplicationController < ActionController::API
  def hello_world
    render json: { text: "Hello World" }
  end
end
```

フロントからのアクセスを許可

```ruby:config/initializers/cors.rb
Rails.application.config.middleware.insert_before 0, Rack::Cors do
  allow do
    origins /localhost\:\d+/

    resource '*',
      headers: :any,
      methods: [:get, :post, :put, :patch, :delete, :options, :head]
  end
end
```

## 1.2. フロントエンドの実装

パッケージインストールしていない場合はインストールして下さい。

```:/react-rails-heroku/projects/react-frontend
$ npm i
```

バックエンドのURLを指定します。
`process.env.REACT_APP_SERVER_URL` で取得できる環境変数になります。

```:.env
REACT_APP_SERVER_URL=http://localhost:5000
```

FetchAPIを使ってバックエンドから値を取得し、表示します。

```javascript:src/App.js
import React, { Component } from "react";
import logo from "./logo.svg";
import "./App.css";

class App extends Component {
  constructor(props) {
    super(props);

    this.state = {
      text: null
    };
  }

  componentDidMount() {
    const fetchInit = {
      method: "GET",
      headers: { "content-type": "application/json" }
    };

    fetch(new URL("hello_world", process.env.REACT_APP_SERVER_URL), fetchInit)
      .then(response => response.json())
      .then(response => this.setState(response));
  }

  render() {
    return (
      <div className="App">
        <header className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <p>Response: {this.state.text}</p>
          <a
            className="App-link"
            href="https://reactjs.org"
            target="_blank"
            rel="noopener noreferrer"
          >
            Learn React
          </a>
        </header>
      </div>
    );
  }
}

export default App;
```

## 1.3. 実行

フロントエンドは `3000` 番ポート、バックエンドは `5000` 番ポートで起動します。

```:/react-rails-heroku/projects/react-frontend
$ npm start
```

```:/react-rails-herokuprojects/rails-backend
$ bundle exec rails s -p 5000
```

[http://localhost:3000/](http://localhost:3000/) にアクセスすると確認できます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/304414/f45f3f6f-6b50-aa64-cae7-022fd8fbadb9.png)

## 1.4. フロントエンドとバックエンドを1コマンドで動かすコマンドを仕込む

### 1.4.1. `npm start` で実行できるようにします。

```:/react-rails-heroku
$ npm init
```

`scripts` に追加していきます。

```json:package.json
"scripts": {
  "start": "bundle exec foreman start"
}
```

`foreman` で実行する為の設定をします。

```:/react-rails-heroku
$ bundle init
```

生成された `Gemfile` に `foreman` を追加し、インストールします。
また、バックエンドの起動に必要な `gem` がここにも必要になります。

```diff:Gemfile
-# gem "rails"
+gem "rails"
+gem "foreman"
+gem 'bootsnap', require: false
+gem 'rack-cors'
+gem 'listen'
+gem 'pg'
```

```:/react-rails-heroku
$ bundle install --path vendor/bundle
```

```:Procfile
backend: exec /usr/bin/env sh -c 'cd projects/rails-backend && npm start'
frontend: exec /usr/bin/env PORT=3000 sh -c 'cd projects/react-frontend && npm start'
```

### 1.4.2. バックエンドも `npm start` で実行できるようにします。

```:/react-rails-heroku/projects/rails-backend
$ npm init
```

`scripts` に追加していきます。

```json:package.json
"scripts": {
  "start": "bundle exec foreman start"
}
```

`foreman` で実行する為の設定をします。

`Gemfile` に `foreman` を入れ、インストールします。

```diff:Gemfile
+gem "foreman"
```

```:/react-rails-heroku/projects/rails-backend
$ bundle install --path vendor/bundle
```

ここで注意ですが、 `Procfile` に記述した起動コマンドでHerokuでも起動されてしまいます。
デフォルトの `bin/rails server -p $PORT -e $RAILS_ENV` で起動させるようにし、この設定は開発環境のみ動くようにします。

```:Procfile.dev
web: exec /usr/bin/env PORT=5000 sh -c 'bundle exec rails s'
```

`foreman` の起動で参照するファイルを `Procfile.dev` にします。
`.foreman` ファイルはローカルでのみ参照されるようにするので、コミットする際は外すようにして下さい。（[`2.1`](https://qiita.com/pure-adachi/private/c2c5730560650c80a5e0#21-%E3%83%90%E3%83%83%E3%82%AF%E3%82%A8%E3%83%B3%E3%83%89%E3%82%92%E3%83%87%E3%83%97%E3%83%AD%E3%82%A4) に除外するコマンドを記載しています。）

```:.foreman
procfile: Procfile.dev
```

実行します。

```:/react-rails-heroku
$ npm start
```

[`1.3`](https://qiita.com/pure-adachi/private/c2c5730560650c80a5e0#13-%E5%AE%9F%E8%A1%8C) と同様に[http://localhost:3000/](http://localhost:3000/) にアクセスすると確認できます。

# 2. Herokuにデプロイする

一旦Herokuにデプロイしてみたいと思います。

CLIでHerokuにログインしておいてください。

```
$ heroku login
```

## 2.1. バックエンドをデプロイ

```:/react-rails-heroku/projects/rails-backend
$ git init
$ git config --local user.name [アカウント名]
$ git config --local user.email [メールアドレス]
$ echo "/vendor/bundle" >> .gitignore
$ echo ".foreman" >> .gitignore
$ git add --all
$ git commit -m "initial commit"
# Herokuアプリを作成する(アプリ名の前にアカウント名を付ける等して、ユニークな名前にして下さい。)
$ heroku create xxxxx-rails-backend
$ git push heroku master
```

バックエンドのアプリケーションが作成されました。
[https://dashboard.heroku.com/apps](https://dashboard.heroku.com/apps)

![image.png](https://qiita-image-store.s3.amazonaws.com/0/304414/87d8e346-1a60-6be5-daa0-9c623e8c3e65.png)

Open app ボタンを押してアクセスすると真っ白の画面が表示されます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/304414/a7c40019-d124-902b-a104-3251ed034aa2.png)

URLの後ろに `hello_world` を付けると `Hello World` が返ってきます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/304414/8e1cfa73-b62a-0def-5ed1-91f293e55806.png)


## 2.2. フロントエンドをデプロイ

```:/react-rails-heroku/projects/react-frontend
$ git init
$ git config --local user.name [アカウント名]
$ git config --local user.email [メールアドレス]
$ git add --all
$ git commit -m "initial commit"
# バックエンド同様アプリ名はユニークな名前にして下さい。
# Create React Appは `buildpack` を使ってHerokuにアプリを作成します。
$ heroku create xxxxx-react-frontend --buildpack mars/create-react-app
$ git push heroku master
```

`mars/create-react-app` の `buildpack` を使ってデプロイするので、
デプロイ後、 `heroku` 上で `build` を自動でしてくれます。
ローカルで `build` してから丸ごとデプロイする必要はありません。

この時以下のようなエラーが出た場合、

```
error Your lockfile needs to be updated, but yarn was run with `--frozen-lockfile`.
```

`yarn install` してコミットし、再度 `push` して下さい

```
$ yarn install
$ git add yarn.lock
$ git commit -m "add yarn.lock"
$ git push heroku master
```

フロントエンドもアプリケーションが作成されました。
[https://dashboard.heroku.com/apps](https://dashboard.heroku.com/apps)

![image.png](https://qiita-image-store.s3.amazonaws.com/0/304414/d896cd89-ff35-6b54-120c-7d142415b248.png)

フロントエンドにアクセスしてみますが、
まだ、レスポンスが受け取れません。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/304414/fd7eb80c-5cb3-3928-e12c-99596ab2af3d.png)

フロントエンドがリクエストを投げている先が `http://localhost:5000/hello_world` となってしまっていることが原因です。

では、開発環境と本番環境でエンドポイントを切り替えるように対応します。

## 2.3. 本番環境のフロントエンドから本番環境のバックエンドを見る

### 2.3.1. フロントエンド

[`1.2`](https://qiita.com/pure-adachi/private/c2c5730560650c80a5e0#12-%E3%83%95%E3%83%AD%E3%83%B3%E3%83%88%E3%82%A8%E3%83%B3%E3%83%89%E3%81%AE%E5%AE%9F%E8%A3%85) で紹介したフロントエンドの対応で指定したエンドポイントは開発環境用でした。
これを本番環境の `production` では `heroku` のバックエンドを見るようにしたい。

本番環境用のバックエンドのURLを指定します。
[`2.1`](https://qiita.com/pure-adachi/private/c2c5730560650c80a5e0#21-%E3%83%90%E3%83%83%E3%82%AF%E3%82%A8%E3%83%B3%E3%83%89%E3%82%92%E3%83%87%E3%83%97%E3%83%AD%E3%82%A4) で紹介した際、アクセスしたURLを設定してください。
これで `production` で動かす場合、`.env.production` の `REACT_APP_SERVER_URL` の環境変数が参照されるようになります。
※厳密には `build` した際、この値が反映される。

```:.env.production
REACT_APP_SERVER_URL=https://xxxxx-rails-backend.herokuapp.com/
```

### 2.3.2. バックエンド

[`1.1`](https://qiita.com/pure-adachi/private/c2c5730560650c80a5e0#11-%E3%83%90%E3%83%83%E3%82%AF%E3%82%A8%E3%83%B3%E3%83%89%E3%81%AE%E5%AE%9F%E8%A3%85) で紹介した、フロントからのアクセスを許可したオリジンは開発環境用でした。
これを本番環境の `production` では `heroku` のフロントエンドも許可したい。
[`2.2`](https://qiita.com/pure-adachi/private/c2c5730560650c80a5e0#22-%E3%83%95%E3%83%AD%E3%83%B3%E3%83%88%E3%82%A8%E3%83%B3%E3%83%89%E3%82%92%E3%83%87%E3%83%97%E3%83%AD%E3%82%A4) で紹介した際、アクセスしたURLを許可してください。

```diff:config/initializers/cors.rb
- origins /localhost\:\d+/
+ origins /localhost\:\d+/, /xxxxx-react-frontend\.herokuapp\.com/
```

## 2.4. 修正をデプロイ

### 2.4.1. バックエンド

```:/react-rails-heroku/projects/rails-backend
$ git add config/initializers/cors.rb
$ git commit -m "update cors origins"
$ git push heroku master
```

### 2.4.2 フロントエンド

```:/react-rails-heroku/projects/react-frontend
$ git add .env.production
$ git commit -m "add .env.production"
$ git push heroku master
```

# 3. Herokuで動かす

[`2.2`](https://qiita.com/pure-adachi/private/c2c5730560650c80a5e0#22-%E3%83%95%E3%83%AD%E3%83%B3%E3%83%88%E3%82%A8%E3%83%B3%E3%83%89%E3%82%92%E3%83%87%E3%83%97%E3%83%AD%E3%82%A4) でアクセスしたURLに再度アクセスします。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/304414/fc5a5570-55a7-8cf8-19bf-6ffccdbbe848.png)

無事、 `Hello World` を取得出来ました。

# おまけ

ローカルでは気軽に `db:migrate:reset` を実行していましたが、 `heroku` でも実行したい場面がありました。

## HerokuでバックエンドのDBを `reset` する方法

あらかじめ `heroku login` でログインしておいて下さい。

```sh
heroku pg:reset DATABASE --app xxxxx-rails-backend
# 確認メッセージが表示されるので、再度プロジェクト名（ここでは `xxxxx-rails-backend` ）を入力
heroku rake db:migrate # マイグレーション実行
heroku rake db:seed # seedを流したい場合
```

# 最後に

今回紹介した、[フロントエンド](https://github.com/pure-adachi/react-frontend)、[バックエンド](https://github.com/pure-adachi/rails-backend)、それらをまとめて動かす[開発パック](https://github.com/pure-adachi/react-rails-heroku)をgithubにあげているので、参考にして頂ければと思います。

個人的に `react` プロジェクトを `heroku` にデプロイする際、 `buildpack` を使うことや、 フロントエンドとバックエンドをまとめて起動するコマンドを仕込む際、バックエンド側で起動に使用している `gem` が開発パック側にも必要となるところで詰まったり、調べることが多かったです。

# 参考文献
* [Github - create-react-app-buildpack](https://github.com/mars/create-react-app-buildpack#quick-start)
* [Qiita - RailsのAPIモードでいい感じにCORS設定をする](https://qiita.com/sugo/items/9c5f9cc5d88e6d7efa2d)
* [Qiita - foreman で アプリケーションを動かす。](https://qiita.com/7kaji/items/6a59977d2ad85604e7fd)