HerokuにPERNアップをデプロイする流れ

### 背景　
* 初めて、Postgresql＋Express＋React+Node.jsでアプリを作った際、[The Stoic ProgrammersのHow to Deploy a PERN app on Herokuという動画](https://www.youtube.com/watch?v=ZJxUOOND5_A)が大変役に立ちました。その時学んだ流れを忘備録も兼ねてまとめます。

### 対象　
* これが分からなかった自分に向けて書いていますので、PERNアプリは作ったが、デプロイは初めてという方むけです。:grinning:

###環境　
* React.js, Express, Postgresql, Node.js, yarnなど。ハードはWindows PC。

###条件　
* Github、Herokuのアカウントは作成ずみ。

###ファイル構成　
* client側はreact-create-appで作り、clientとserverというフォルダーに分けて内容を作成。それぞれでyarn initをしてpackage.jsonがある。これをデプロイしていきます。

##0. 全体の流れ

ここで紹介するデプロイの流れは大まかに言って以下の通りです。

1. Herokuがアプリに入る入口を準備
1. Herokuが読むstaticファイルを準備
1. ポートの準備（Proxyを含む）
1. Heroku上のPostgresqlとつなぐ準備 *
1. Node.jsとyarn/npmのバージョンを指定
1. 違ったURLが入力されたときの対応を準備
1. Heroku上でPostgresqlを作る *
1. 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を読めるようになりました。:thumbsup:

## 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を加えた変更を活かすために以下の手順を行います。

1. client側のnode_modulesとyarn.lockをいったん削除。
2. もういちどdependenciesを導入。
3. clientを再起動。

## 8.　.envを準備する。(データベースを使う場合に必要）

1. 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管理下に置かないようにします！

でも、大丈夫:grinning:。もし誤って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だと動かないときに解決できた方法」](https://qiita.com/miomaeshima/items/cf3ff74d1ee497d2960a)にまとめています。

これができていないと、fetchがうまくいきません。:scream:


```
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上のデータベースとつながります！:ok_woman:

## 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上でアプリが出来上がりますので、実際に確認してください。:clap:

## 14.　今後、Heroku上のアプリを更新する際は？
 
ローカルでコードを更新したら、git add, git commit して

```
git push heroku main
```

でOKです！

___
参照した動画 [How to Deploy a PERN application on Heroku](https://www.youtube.com/watch?v=ZJxUOOND5_A)