フロントはReact,バックはNode.jsそしてHerokuにデプロイ
本文書ではフロントエンドにReact、バックエンドにNode.js(Express)を使って構築したアプリケーションをHerokuにデプロイするまでの方法を説明しています。
問題なく構築したアプリケーションをHerokuにデプロイできれば気にすることはありませんが必ずしもうまくいくわけではなくエラーが発生した場合は各自でデプロイの問題を解決しなければならない時もあります。その際どうやってログを見ればいいのか、デプロイのキャンセルはどうやって行うかも説明を行っています。
node.js, npm, gitがインストールされているmacOS環境で動作確認を行っています。手順通りに進めればHerokuにReact+Node.jsのアプリケーションをインターネット上に公開することができます。
目次
環境の構築
任意の名前のプロジェクトフォルダを作成してください。ここではfront_react_back_nodeとしています。作成したfront_react_back_nodeに移動してnpm init -yコマンドを実行してください。
% mkdir front_react_back_node
% cd front_react_back_node
% npm init -y
npm init -yコマンドを実行したfront_react_back_nodeフォルダには、package.jsonファイルが作成されます。
バックエンドExpressサーバの構築
最初にExpressサーバの構築を行ってきます。front_react_back_nodeのフォルダにbackendフォルダを作成してください。
Expressライブラリのインストールを行います。
% npm install express
インストール完了後にbackendフォルダを作成します。作成後backendフォルダに移動してindex.jsファイルを作成してください。
% mkdir backend
% cd backend
index.jsファイルにはExpressサーバ稼働に必要なコードを記述します。PORTについてはデプロイするHeroku側でポートが設定できるようにprocess.env.PORTを設定します。環境変数が取得できない場合は指定した3000で起動します。
const express = require('express')
const app = express()
const port = process.env.PORT || 3000
app.get('/', (req, res) => {
res.send('Hello World!')
})
app.listen(port, () => {
console.log(`listening on *:${port}`);
})
front_react_back_nodeフォルダからExpressサーバを起動します。nodeコマンドには作成したbackend/index.jsファイルを指定してくてください。
% node backend/index.js
listening on *:3000
ブラウザからlocalhost:3000にアクセスするとHello World!が表示されます。Expressサーバが稼働していることが確認できます。
package.jsonファイルを開いて、scriptsにstartをを追加します。
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "node backend/index.js"
},
追加後はnpm startでExpressサーバを起動することができます。
% npm start
APIのエンドポイントの作成
フロントエンドReactからバックエンドExpress.jsサーバにfetch(またはaxios)を利用してアクセスするためアクセス先のエンドポイントをExpressサーバ側に準備します。/(ルート)にブラウザからアクセスすると文字列を戻していましたが、JSONで戻すように設定を行います。エンドポイントのURLは/apiとしています。
const express = require('express')
const app = express()
const port = 3000
app.get('/', (req, res) => {
res.send('Hello World!')
})
app.get("/api", (req, res) => {
res.json({ message: "Hello World!" });
});
app.listen(port, () => {
console.log(`listening on *:${port}`);
})
ブラウザから/apiにアクセスするとJSONで戻されていることが確認できます。
エンドポイントの動作確認ができたのでReact側からこのエンドポイントにアクセスすることができるはずなので次はフロントエンドのReactの設定を行っていきます。
フロントエンドReactの設定
front_react_back_nodeフォルダにReactプロジェクトを作成します。プロジェクトフォルダの名前にはfrontendとつけています。これでfront_react_back_nodeフォルダにはExpressサーバのbackendとReactのfrontendのフォルダが存在することになります。
% npx create-react-app frontend
作成したfrontendに移動してReactを起動します。Reactを起動するためにnpm startコマンドを実行するとport3000が使用済みであるというメッセージが表示されます。これはExpressサーバで同じポート3000を利用しているためです。
%cd frontend
%npm start
・
・
? Something is already running on port 3000. Probably:
/usr/local/bin/node backend/index.js (pid 9200)
in /Users/mac/Desktop/front_react_back_node
他のポートで起動するか聞かれまずが”n”を選択します。もし”Y”を選択して場合は3000ではなく3001のポートでReactが起動します。
ReactではなくExpressサーバのポートを変更します。ここでは3001に設定します。
const express = require('express')
const app = express()
const port = process.env.PORT || 3001
//略
Expressサーバのポートを3001に変更して、再度npm startを実行すると下記の画面が表示されます。
Reactの初期画面を変更するためにfrontend¥src¥App.jsを以下のように更新します。
import './App.css';
function App() {
return (
<div className="App">
<h1>フロントエンド</h1>
</div>
);
}
export default App;
ブラウザで確認すると以下の画面が表示されます。
ReactからExpressサーバへのアクセス
useState, useEffectを利用
バックエンドのExpressサーバからデータを取得するためにfetchメソッドを利用しますが、fetchメソッドを利用する前にReactのuseState, useEffectを確認します。
useStateは変数を定義し値を保存することができるHookです。useEffectはコンポーネントをレンダリングする際にuseEffect内で設定した関数を実行することができるHookです。この2つを利用してExpressサーバからデータを取得します。
useEffectを利用しているのでレンダリング時に一度useEffectの中身が実行されます。fetchメソッドではURLにlocalhost:3001を指定しています(Expressサーバのポート)。取得したデータをuseStateで定義したmessageに設定します。Reactのプロジェクトフォルダであるfontend/srcのApp.jsを更新します。
import './App.css';
import { useState,useEffect } from 'react'
function App() {
const [message, setMessage] = useState('');
useEffect(() =>{
fetch('http://localhost:3001/api')
.then((res) => res.json())
.then((data) => setMessage(data.message));
},[])
return (
<div className="App">
<h1>フロントエンド</h1>
<p>{ message }</p>
</div>
);
}
export default App;
実際にブラウザでReactにアクセスしてもmessageは表示されません。デベロッパーツールのコンソールを見るとエラーを確認することができます。
エラーの内容ではlocalhost:3000からlocalhost:3001/apiへのfetchはCORSポリシーによってブロックされていると表示されています。
この問題を回避するためにReactのpackage.jsonファイルにproxyを設定します。
{
"name": "frontend",
"version": "0.1.0",
"private": true,
"proxy": "http://localhost:3001",
//略
proxyを設定するのでApp.jsのfetchメソッドのURLもApp.jsに設定したhttp://localhost:3001/apiから/apiに変更することができます。
useEffect(() =>{
fetch('/api')
.then((res) => res.json())
.then((data) => setMessage(data.message));
},[])
再度ブラウザでアクセスするとバックエンドサーバのエンドポイント/apiにアクセスして取得したHello Worldを確認することができます。
proxyを設定することによって何が行っているのか確認しておきます。app.jsファイルではfecth先を/apiを設定しているのでlocalhost:3000/apiへのアクセスが行われます。デベロッパーツールのネットワークタブでRequest URLを確認してもhttp://localhost:3000/apiにGETメソッドが送信されていることが確認できます。
http://localhost:3000/apiにアクセスしているように見えるので表面的にはわかりませんが、裏側ではReact側のproxyの設定によってhttp://localhost:3001/apiにアクセスが行われています。
ここまでの設定でフロントエンドのReactからバックエンドのExpressサーバに接続を行いデータを取得できることが確認できました。
Herokuへのデプロイの準備
Expressサーバにブラウザからアクセスがあった場合にReactの内容を表示できるように設定を行っていきます。
指定したパス内の静的ファイルをExpressサーバで扱えるようにexpress.staticを利用します。Reactのindex.htmlファイルはReactのビルドを行うとfrontend/buildの下に作成されるのでindex.jsファイルから存在するfront_react_back_nodeのパスを設定します。
app.use(express.static(path.join(__dirname, '../frontend/build')));
Expressサーバの/api以外のアクセスはすべてReactに渡せるように*(アスタリスク)設定も追加します。
const express = require('express')
const app = express()
const path = require('path');
const port = process.env.PORT || 3001;
app.use(express.static(path.join(__dirname, '../frontend/build')));
app.get("/api", (req, res) => {
res.json({ message: "Hello World!" });
});
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname,'../frontend/build/index.html'));
});
app.listen(port, () => {
console.log(`listening on *:${port}`);
})
frontendフォルダに移動して、Reactのビルドを行います。
% npm run build
ビルドが完了するとfrontendフォルダにbuildフォルダが作成されその中にindex.htmlファイルが作成されていることが確認できます。
buildが完了したら、front_react_back_nodeでnpm startコマンドを実行してください。
% npm start
> front_react_back_node@1.0.0 start
> node backend/index.js
listening on *:3001
ブラウザでlocalhost:3001にアクセスすると下記の画面が表示されます。
最後にHeroku上でビルドを行えるようにfront_react_back_nodeのpackage.jsonファイルのscriptsに以下を追加します。
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "node backend/index.js",
"heroku-postbuild": "cd frontend && npm install && npm run build"
},
Herokuのデプロイ
Herokuのアカウント作成
Herokuへのデプロイを行うためにはHerokuのアカウントの作成とHeroku CLIのインストールが必要となります。
Herokuのアカウントの作成とHeoku CLIのインストールについてはこちらで解説を行っています。
Heroku CLIとgitを使ってデプロイ
Heroku CLIとgitコマンドを利用してHerokuのデプロイを行います。
frontendフォルダに移動して、.gitフォルダを削除してください。.gitフォルダが存在するとHerokuでReactのビルドが行われません。
% rm -rf .git
git initコマンドをfront_react_back_nodeフォルダで実行してgitリポジトリの初期化を行います。実行すると.gitフォルダが作成されます。
% git init
git add .コマンドで実行したフォルダ下のすべてのファイルをステージングエリアに移動しcommitの対象とした後、commitでメッセージをつけてリポジトリに登録します。
% git add . && git commit -m "init"
Herokuへのログインを行います。
% heroku login
heroku: Press any key to open up the browser to login or q to exit:
ログインが完了したら、任意の名前のプロジェクトをHeroku上に作成します。
% heroku create front-react-back-node
Creating ⬢ front-react-back-node... done
https://front-react-back-node.herokuapp.com/ | https://git.heroku.com/front-react-back-node.git
git pushコマンドを利用してHerokuのリモートリポジトリにローカルのファイルをプッシュします。remote: Verifying deploy… done.が表示されることを確認します。
% git push heroku master
//略
remote: Verifying deploy... done.
To https://git.heroku.com/front-react-back-node.git
* [new branch] master -> master
デプロイが成功するとローカルで動作確認したようにフロントエンドの文字列の下にバックエンドのExpress.jsの/apiにfetchでアクセスして取得したHello World!が表示されます。
URLはlocalではなくfront-end-back-node.herokuapp.comという名前になっており、インターネット上に公開されています。公開が完了しているのでどこからでもアクセスすることが可能となります。
ブラウザにNot Foundが出た場合
デプロイが成功したのにも関わらずブラウザでアクセスするとNot Foundが表示された場合はエラーログを確認します。コマンドラインであればherokuにロウインした状態でheroku logsで確認することができます。ログが出力されるのでメッセージを確認してください。問題の原因となるヒントがあるはずです。
% heroku logs
ダッシュボードであれば右側のOpen appボタンの右にあるMoreをクリックするとメニューが表示されるので、View logsを選択します。
エラーメッセージの中にError: ENOENT: no such file or directory, stat ‘/app/frontend/build/index.html’が出力されReactで作成したコンテンツが表示されない場合はindex.jsのexpress.staticで設定したパスが間違っていないかまたReactのfrontendフォルダのビルドが行われていない可能性があります。その場合はHerokuに接続しコマンドラインを利用して指定しているファイルが存在するか確認することができます。このようにコマンドラインを利用してHerokuにアクセスすることも可能です。
% heroku login
% heroku run bash -a front-react-back-node
Running bash on ⬢ front-react-back-node... up, run.7617 (Free)
%ls
backend frontend node_modules package-lock.json package.json
%cd frontend
$ ls
README.md build node_modules package-lock.json package.json public src
Herokuのビルドの停止
途中何かの記述ミスがによりビルドが終わらない場合はコマンドラインを利用してビルドを停止することができます。コマンドラインで停止するためにはheroku-buildsをインストールします。
% heroku plugins:install heroku-builds
Installing plugin heroku-builds... installed v0.0.29
下記のコマンドで現在のビルドの状況を確認することができます。オプション-aの後ろにプロジェクト名を指定します。
% heroku builds:info -a front-react-back-node
=== Build 71154f89-3ab8-4830-8932-ce2a9adfff19
By: orangesky2021@gmail.com
Status: pending
When: 2021-02-13T00:54:59Z
ビルドのキャンセルは以下のコマンドで実行します。こちらも-aの後ろにプロジェクト名を指定します。
% heroku builds:cancel -a front-react-back-node
Stopping build 71154f89-3ab8-4830-8932-ce2a9adfff19... done
再度heroku builds:infoコマンドでステータスを確認するかブラウザからHerokuにアクセスし、latest ActivityをみてBuild failedが表示されていればキャンセルは完了しています。