79
Help us understand the problem. What are the problem?

More than 3 years have passed since last update.

posted at

updated at

Organization

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

はじめに

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

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

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

  1. ローカルで動くものを作る
  2. Herokuにデプロイする
  3. 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

ルートを追加

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

hello_world メソッド定義

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

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

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を使ってバックエンドから値を取得し、表示します。

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/ にアクセスすると確認できます。

image.png

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

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

/react-rails-heroku
$ npm init

scripts に追加していきます。

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

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

/react-rails-heroku
$ bundle init

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

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 に追加していきます。

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

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

Gemfileforeman を入れ、インストールします。

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 に除外するコマンドを記載しています。)

.foreman
procfile: Procfile.dev

実行します。

/react-rails-heroku
$ npm start

1.3 と同様に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

image.png

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

image.png

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

image.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-appbuildpack を使ってデプロイするので、
デプロイ後、 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

image.png

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

image.png

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

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

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

2.3.1. フロントエンド

1.2 で紹介したフロントエンドの対応で指定したエンドポイントは開発環境用でした。
これを本番環境の production では heroku のバックエンドを見るようにしたい。

本番環境用のバックエンドのURLを指定します。
2.1 で紹介した際、アクセスしたURLを設定してください。
これで production で動かす場合、.env.productionREACT_APP_SERVER_URL の環境変数が参照されるようになります。
※厳密には build した際、この値が反映される。

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

2.3.2. バックエンド

1.1 で紹介した、フロントからのアクセスを許可したオリジンは開発環境用でした。
これを本番環境の production では heroku のフロントエンドも許可したい。
2.2 で紹介した際、アクセスしたURLを許可してください。

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 でアクセスしたURLに再度アクセスします。

image.png

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

おまけ

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

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

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

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

最後に

今回紹介した、フロントエンドバックエンド、それらをまとめて動かす開発パックをgithubにあげているので、参考にして頂ければと思います。

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

参考文献

Register as a new user and use Qiita more conveniently

  1. You can follow users and tags
  2. you can stock useful information
  3. You can make editorial suggestions for articles
What you can do with signing up
79
Help us understand the problem. What are the problem?