背景
- 初めて、Postgresql+Express+React+Node.jsでアプリを作った際、The Stoic ProgrammersのHow to Deploy a PERN app on Herokuという動画が大変役に立ちました。その時学んだ流れを忘備録も兼ねてまとめます。
対象
- これが分からなかった自分に向けて書いていますので、PERNアプリは作ったが、デプロイは初めてという方むけです。
###環境
- React.js, Express, Postgresql, Node.js, yarnなど。ハードはWindows PC。
###条件
- Github、Herokuのアカウントは作成ずみ。
###ファイル構成
- client側はreact-create-appで作り、clientとserverというフォルダーに分けて内容を作成。それぞれでyarn initをしてpackage.jsonがある。これをデプロイしていきます。
##0. 全体の流れ
ここで紹介するデプロイの流れは大まかに言って以下の通りです。
- Herokuがアプリに入る入口を準備
- Herokuが読むstaticファイルを準備
- ポートの準備(Proxyを含む)
- Heroku上のPostgresqlとつなぐ準備 *
- Node.jsとyarn/npmのバージョンを指定
- 違ったURLが入力されたときの対応を準備
- Heroku上でPostgresqlを作る *
- gitとHerokuを繋いでデプロイ!
"*"印はPostgresqlを使わないアプリは必要なし。では、参りましょう。
1. Herokuがアプリに入る入口を準備。
Herokuはこのアプリに入るとき、まず、ルートディレクトリにあるserver側のpackage.jsonを見ます。つまり、server側のpackage.jsonがルートディレクトリにないといけません。
私の場合はserverフォルダーを作り、その中にpackage.jsonが入っていましたので、これでは見つかりません。そこで、server側のファイルをすべてルートディレクトリに移しました。
- 最初のディレクトリ構成
root/
├ client/
│ ├ 色々/
│ ├ 色々/
│ └ package.json
│
└server/
├ 色々/
├ 色々/
└ package.json
- 変更後のディレクトリ構成
root/
├ client/
│ ├ 色々/
│ ├ 色々/
│ └ package.json
│
├ 色々/
├ 色々/
└ package.json
これでHerokuがserver側のpackage.jsonを読めるようになりました。
2. gitの設定
まだしていないなら、git initしておきます。(不安ならもう一度してOK)
ここで、.gitファイルが、このアプリ全体の中で一つだけ、そして、ルートディレクトリにあることを確認します。私は、Windowsの場合はターミナルで隠しファイルを出す方法が分からなかったので(💦)エクスプローラーでチェックしました。clientフォルダーに.git fileがあったら削除してください。
3. staticファイル(= build)を作る。
Herokuが読み込むのは「staticファイル(静的ファイル、buildとも言う)というモノ」なので、まずそのstaticファイルを作らなければなりません。ローカルで開発しているとき書いているpublicフォルダーに入っているコードから、staticファイルを作ることをbuildすると言います。
create-react-appでクライアント側を作った場合、package.jsonの中のscriptsの中に、buildするscriptがあるのでこれを使います。
具体的には、clientフォルダーにcdして、ターミナルで yarn build
これで、レポジトリ内にbuildができています。中を開いてみると、publicフォルダーの各ファイルの内容がぎっしり入ったファイルがあります。この中にhtmlも含まれていて、Herokuはこれを表示します。
##4. Herokuにbuildの指示をする。
さて、今は自分でbuildをしたわけですが、Heroku上では自動的にbuildされないといけないので、Herokuがまず最初に読むserver側のpackage.jsonに、その指示を書いておきます。
scriptsの中に「clientに入って、dependenciesを導入して、buildして」という一文を加えます。
"scripts": {
"start": "node index.js",
"heroku-postbuild": "cd client && yarn && yarn build" //←これを加える。
},
4. staticの場所をHerokuに伝える。
上のscriptでstaticができるわけですが、Herokuがserverのメインファイル(package.jsonにあるmainのファイルと名前を揃えておく)を読み込んでいくときに、そのstaticがどこにあるというのを伝えます。
その指示の際、pathというものを使います。そもそもNode.jsに入っているのでdependencyとしてyarn addとかしなくてOKで、ただファイルの頭のところで以下の一行を足します。
const path = require("path");
そして、以下のように本番はstaticを使うよう、また場所はどこかを教えます。ちなみにdirnameはdirectory nameのことなので、ルート名/client/buildにあるよ、ということです。)
if (process.env.NODE_ENV === "production"){
app.use(express.static(path.join(__dirname, "client/build")))
}
5. PORTの準備。(サーバー側)
ローカルで開発中、serverが走っているポートは例えばlocal host 5000だったりするわけですが、Herokuにしたらそんなのどこ?!です。本番用のポートはprocess.env.PORTと書けばHerokuはわかるので、今まで5000などと書いていたら、以下のように書き換えます。
まずPORTという変数を作ります。下の一行をファイルの最初のほうで足します。PORTと言ったら、まずはHerokuの使うポートを探して、なかったら(=ローカル環境ということなので)5000を使ってね。という意味です。
const PORT = process.env.PORT || 5000
そして、app.listenのところを、
app.listen(PORT, () => {
console.log(`Server is starting on port ${PORT}`);
});
と書き換えます。
6. proxyの準備。(クライアント側)
portについて今度はclient側で準備します。clientからバックエンドにアクセスるときのポートについて、ローカル環境中ではこのポートと伝えつつ、同時に、Herokuには本番で使われるべきportを使うように伝えます。
やり方は、client側のpackage.jsonの末尾にproxyというのを設定して、そこにローカル環境で使うポートを書きます。
"proxy":"http://localhost:5000"
}
proxy設定をしたので、clientのファイルにあるhttp://localhost:5000
は削除します。残ったコードは"/"
や "/xxx"
のような形になります。
7. クライアントをリフレッシュ
この段階でアプリを表示し、入力して動かそうとすると、proxyが効かずエラーが発生します。(clientがloalhost:3000で走っていた場合は"/"がproxyで設定したポートではなくlocalhost:3000になってしまう。)
そこでproxyを加えた変更を活かすために以下の手順を行います。
- client側のnode_modulesとyarn.lockをいったん削除。
- もういちどdependenciesを導入。
- clientを再起動。
8. .envを準備する。(データベースを使う場合に必要)
- server側でdotenvを導入する。
2. .envファイルを作る。ただ以下の通りに書きます。jsファイルではないのでrequireも何もいりません。
PG_USER = postgres
PG_PASSWORD = xxxxxx
PG_HOST = localhost
PG_DATABASE = xxxxxx
PG_PORT = 5432
**コロン(:)ではなくイコール(=)**を使います。
git add, git commitをする前に.envを.gitignoreに入れてgit管理下に置かないようにします!
でも、大丈夫。もし誤ってgit管理下に入れてしまったときには、
git rm --cached [name of file]
としておいて、git add, commit, pushしてください。
9. 本番時、Heroku上のpostgresqlとつながるよう設定。(Postgresqlを使う場合のみ必要)
開発中は.envに書いたデータをもとにローカルのpostgresqlにアクセスし、本番中はprocess.env.DATABASE_URL、つまり、Heroku上で設定するpostgresqlにアクセスするように書きます。
devConfigはローカルのpostgresqlデータベース、proConfigは本番環境のアクセス先で定義し、本番のときはproConfigにアクセスしてね、と指示します。
さらにSetting SSL~の部分は、Herokuの場合、Postgresqlを使うときにはSSL接続が必要だからです。これは別記事の「ローカルで動くNode.jsとPostgresqlを使ったアプリがHerokuだと動かないときに解決できた方法」にまとめています。
これができていないと、fetchがうまくいきません。
const Pool = require("pg").Pool;
require("dotenv").config(); // これで.envのデータが読めるようになる。
const devConfig = `postgresql://${process.env.PG_USER}:${process.env.PG_PASSWORD}@${process.env.PG_HOST}:${process.env.PG_PORT}/${process.env.PG_DATABASE}`
const proConfig = process.env.DATABASE_URL
const pool = new Pool({
connectionString : process.env.NODE_ENV === "production" ? proConfig : devConfig,
Setting SSL
ssl:{
sslmode: 'require',
rejectUnauthorized: false
}
})
module.exports=pool;
これで後で設定するHeroku上のデータベースとつながります!
10. Node.jsとyarn/npmのバージョンを指定。
開発中使ったのと違うバージョンのNode.jsやyarn/npmが、本番環境で使われてエラーの元とならないよう、開発中と同じバージョンを使ってください、と指示するため、server側のpackage.jsonにengines(engines 複数!)というものを足します。
{
"name": "xxxxx",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"engines":{ //この三行です。バージョンはターミナルでnode -v、yarn -vで調べます。
"node": "12.19.0",
"yarn": "1.22.5"
},
"scripts": {
"server": "nodemon index",
"start": "node index.js",//Add this one
"heroku-postbuild": "cd client && yarn && yarn build" //Also this.
},
"dependencies": {
"cors": "^2.8.5",
"dotenv": "^8.2.0",
"express": "^4.17.1",
"nodemon": "^2.0.7",
"pg": "^8.5.1"
}
}
11. キャッチオールを足す。(必須ではない)
間違った末尾のURLでアクセスされた場合、トップページを返しておくように、server側、index.jsのapp.listen直前に足しておきます。
app.get("*", (req, res)=>{
res.sendFile(path.join(__dirname, "client/build/index.html"));
})
12. Herokuの準備!
【注意】ここまで色々コードをいじっているので、git commitしておくこと!
いよいよ、Herokuの準備です。
まず、ルートディレクトリやフォルダー内の.gitignoreに.envやnode_modulesを入れているか今一度、チェック。これまで入れていなかったら、gitに入れていたということなので、
git rm --cached.env
で消します!
まず、ターミナル上でHerokuにログインします。
heroku login
どんなキーでもよいので(qは出てしまうのでそれ以外)たたくとHerokuのサイトが現れるのでログイン。ターミナルに戻り、
heroku create [name of app]
これで空のアプリができました。
Heroku上でpostgreqlデータベース(無償版)を作ります。 (もちろんPostgresqlを使う場合のみ)
heroku addons:create heroku-postgreql:hobby-dev -a [name of app]
作ったデータベースに入ります。
heroku pg:psql -a [name of app]
データベース中にテーブルを作ります。
ここは通常、ターミナルでpostgreqlのテーブルと作るのと同じです。
13. Herokuとgit remoteを繋ぐ。
heroku git:remote -a [name of app]
そして、、、、
git push heroku main
以上です!お疲れさまでした。
Heroku上でアプリが出来上がりますので、実際に確認してください。
14. 今後、Heroku上のアプリを更新する際は?
ローカルでコードを更新したら、git add, git commit して
git push heroku main
でOKです!