ExpressでJWT認証機能付きのAPIを作成する（その1. XserverにExpressサーバを立ててgitで更新出来るようにする）

# はじめに
業務でFileMaker Cloudを使用しているのですが，不特定多数のユーザを対象にwebからデータベースへアクセスさせる必要が生じました。FileMakerには「webdirect」というデータベースのレイアウトをそのままweb公開させる仕組みがあり，従来そのような不特定多数からのアクセスをさせる場合にはそれ専用のレイアウトを作成してゲストアカウントで接続するようにしていました。

しかし最新のクラウドベースサーバであるFileMaker Cloudにはゲストアカウントが設定できないため，従来のweb directを利用したアクセスが出来ません。一方，「FileMaker Data API」というデータベースに外部からアクセスするためのAPIは用意されています（もちろん認証は必要ですが）。したがって

　　[Webページ]　→（FileMakerで認証）→　[FileMaker Data API]

という形での実装を考えましたが，FileMaker Data APIには[CORS制限](https://developer.mozilla.org/ja/docs/Web/HTTP/CORS)がかけられているため，フロントエンドから直接APIを叩くことが出来ません。また，そもそもこれではFileMaker Data APIにアクセスするための認証データをフロントエンド側に保持させる必要があり，セキュリティ上問題が残ります。そこで

　　[Webページ]　→（独自に認証）→　**[中継サーバ]**　→（FileMakerで認証）→ [FileMaker Data API]

という形で中継サーバを立て，そのサーバからFileMaker Data APIへアクセスさせることにしました。

JavaScriptは多少触ったことはあるけれども，今まで本格的なバックエンド開発の経験は無かった筆者が試行錯誤しながらサービスを構築していった過程を共有することで，同じような悩みを持つ方の参考になればと思い公開します。

思いのほか内容が膨大になってしまったので，いくつかの記事に分けてあります。この記事はまず開発環境を整えるまでの内容です。

## 目的
Xserverのサーバ上にExpressを利用したサーバを立て，その内容をgitで更新できるようにする。

## 開発環境
既にXserverにはnode/npmがインストールされている前提です。インストールは以下のページなどを参考にしてください。

https://qiita.com/yuu_1st/items/95865adfbe5ddb3eeeb0

開発時点でのバージョンは
　　**node** v14.17.4
　　**npm** 6.14.14
でした。

また，ローカル開発環境ではGitの初期設定が終わっていて[GitHub Desktop](https://desktop.github.com/)がインストールされている前提です。

# Step 1. ローカルにgitリポジトリを作成し，Xserver側でpull出来るようにする
## 1-1. ローカルにgitリポジトリを作ってリモートに紐付け
GitHub Desktopで`New Repository...`を選べばローカルでフォルダ作って`git init`からリモートリポジトリ作成までやってくれる。`.gitignore`は`Node`を選んでおこう。作成したらひとまず`Publish repository`でGitHubへアップ。

## 1-3. Xserver側でgit pull出来るようにする

先にXserverのgitも下記を参考に最新版にしておく。

https://qiita.com/y-ngi/items/7a170a19d809d2f49a15 

GitHubへSSH接続するために，Xserverのsshで公開鍵と秘密鍵を作成しておく。参考↓

https://zenn.dev/kazweda/scraps/c1679adbcfdcdd

`~/.ssh`ディレクトリに移動して以下：
```bash:xserver ssh
$ ssh-keygen -t rsa -b 4096 -C "hoge@sample.com"
```
最後のはコメントなので適当でOK。途中プロンプトでファイル名やパスフレーズが聞かれるが，これも入力せずEnterでOK。作成された`id_rsa.pub`ファイルをviか何かで開いて内容をコピーし，GitHubの"Settings" > "SSH and GPG keys" > "SSH keys" > "New SSH key"から登録しておく。

先に作成したリモートリポジトリのページに入り，"Code"ボタンからSSHクローン用の文字列を取得。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1734470/ea69a917-2396-0c83-79a1-1c6eeff28b9a.png)

リポジトリを格納したいディレクトリの中で（今回は`public_html`と同階層に`node`というディレクトリを作成した），以下を実行する。
```bash:xserver ssh
$ git clone [GitHubからコピーした文字列]
```
次のように表示されればOK。
```
Cloning into '[リポジトリ名]'...
Warning: Permanently added the ECDSA host key for IP address 'xxx.xxx.xxx.xxx' to the list of known hosts.
remote: Enumerating objects: 5, done.
remote: Counting objects: 100% (5/5), done.
remote: Compressing objects: 100% (5/5), done.
Receiving objects: 100% (5/5), done.
remote: Total 5 (delta 0), reused 5 (delta 0), pack-reused 0
```

## 1-4. Hello Worldでも作って試してみる。
ローカルで適当にjavascriptファイルを作ってみる。
```javascript:test.js
console.log("Hello, World!")
```
作成したファイルをリモートにpushしてから，Xserver側でpull。
```zsh:xserver ssh
$ git pull
$ node test
Hello,world!
```
うまくいったっぽい。

# Step 2. とりあえずExpressサーバを立ててみる
Node.jsのwebフレームワークとしては[Express](https://expressjs.com/ja/)を使用することとした。Expressで超シンプルなサーバを立てて，Webからアクセス出来るようにしてみる。

## 2-1. サーバ作るよ
とりま，[Expressの公式Hello World](https://expressjs.com/ja/starter/hello-world.html)をそのまま使ってみる。
```zsh:zsh
% npm init
% npm install express --save
```
```javascript:server.js
const express = require('express')
const app = express()
const port = 3000

app.get('/', (req, res) => {
  res.send('Hello World!')
})

app.listen(port, () => {
  console.log(`Example app listening on port ${port}`)
})
```
server.jsを保存してから
```ｚsh:zsh
$ node server
Example app listening on port 3000
```

ここでとりあえずターミナルからcurlでlocalhost:3000を叩いてみると
```zsh:zsh
% curl -w'\n' localhost:3000
Hello World!
```
うん，出来てるね。

ではXserver側でもやってみる。

Xserverにsshでログインして
```bash:xserver ssh
$ git pull
$ npm install
$ node server
Example app listening on port 3000
```

サーバは問題なく起動しているが，この時点でxserverのドメインに3000番ポートでアクセスしてもページは表示されない。Xserverではデフォルトの80/443番ポート**以外**への外部からのアクセスが出来ないためだ。なので，内部でそちらに転送してやる必要がある。

Xserverのpublic_htmlディレクトリにある.htaccessファイルで転送を設定する。Wordpressを利用しているサーバだと，Wordpressにより自動的に追加された設定が書き込まれているので，そちらではなく上の方に記述すること。
```diff_apache:.htaccess
SetEnvIf Request_URI ".*" Ngx_Cache_NoCacheMode=off
SetEnvIf Request_URI ".*" Ngx_Cache_AllCacheMode
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{HTTPS} !=on [NC]
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
+ RewriteRule ^fmproxy/(.*)$ http://localhost:3000/$1 [P,L]
</IfModule>
# BEGIN WordPress
    (略)
# END WordPress
```
RewriteRuleの詳しい内容は↓このあたりを参照。

https://weblabo.oscasierra.net/apache-rewrite-2/

これで hogehoge.com/fmproxy/ 以下の全てのアクセスは localhost:3000/ 以下にリダイレクトされることになる。

.htaccessを保存してインターネットからXserverのサイトにアクセスしてみる。

![スクリーンショット 2022-03-15 21.35.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1734470/0295b3f6-68aa-7f39-3f2d-9433539a9a32.png)

成功。

## 2-2. Daemon化するよ。
Node.jsで立てたサーバはそのままだとエラーで落ちてしまうので，自動で再起動できるようDaemon化する。Daemon化にはforeverを使うことが多いようだけど，[foreverのnpm](https://www.npmjs.com/package/forever)を見ると「新規にインスコするなら**pm2**か**nodemon**使いな〜」と書いてある。
> Note that this project currently fully depends on the community for implementing fixes and new features. For new installations we encourage you to use pm2 or nodemon

なので大人しく pm2 と nodemon を使う。

pm2 はプロセスの状態を視覚的に管理できたりする一方，`console.log`の出力がターミナルへ直接出てこなかったりするので try & error を繰り返す開発環境には不向き。よって開発環境では nodemon を使い，本番環境では pm2 を使うのが良さそうである。

```zsh:zsh
$ npm install pm2 -g
$ npm install nodemon -g
```
pm2の解説見ると`sudo`でインストールしていることが多いけど，Xserverで`sudo`を付けずにインストールしても問題無かった。

インストールされたら
```zsh:xserver ssh
$ pm2 start server.js
```
で pm2 による管理下にプロセスが立ち上がる。
```
[PM2] Starting /home/hogehoge/hogehoge.com/node/fmproxy/server.js in fork_mode (1 instance)
[PM2] Done.
┌────┬────────────────────┬──────────┬──────┬───────────┬──────────┬──────────┐
│ id │ name               │ mode     │ ↺    │ status    │ cpu      │ memory   │
├────┼────────────────────┼──────────┼──────┼───────────┼──────────┼──────────┤
│ 0  │ server             │ fork     │ 0    │ online    │ 0%       │ 30.0mb   │
└────┴────────────────────┴──────────┴──────┴───────────┴──────────┴──────────┘

```
開発環境でファイルの変更を自動的にモニタして再起動して欲しければ`--watch`オプションをつけて起動すればよし。ついでに`package.json`のスクリプトも変えておく。
```json:package.json（該当部分のみ）
  "scripts": {
    "start": "pm2 start server --watch",
    "stop": "pm2 stop server",
    "restart": "pm2 restart server",
    "test": "nodemon server"
  }
```
これで`npm test`でテスト用にnodemonが立ち上がり，`npm start` `npm stop` `npm restart`でそれぞれpm2の制御が可能となった。

## 2-3. 環境変数を使えるようにしておく
ローカルの開発環境と本番とでデータベースへのアクセス情報が違ったりするので，今のうちに`.env`ファイルを使えるようにしておく。
```zsh:zsh
$ npm install dotenv --save
```
プロジェクトのルートディレクトリに`.env`ファイルを作成。とりあえずこんな内容で。
```:.env（開発環境用）
NODE_ENV=development
```
`server.js`をちょっといじって，`/`にアクセスされたらOKステータスと環境を返すようにしてみる。
```diff_javascript:server.js
+ require('dotenv').config()
const express = require('express')
const app = express()
const port = 3000

app.get('/', (req, res) => {
-   res.send('Hello World!')
+   res.json({
+     status:"OK",
+     mode:process.env.NODE_ENV
+   })
})

app.listen(port, () => {
-   console.log(`Example app listening on port ${port}`)
+   console.log(`App listening on port ${port}`)
})
```
この状態で curl から`localhost:3000`にアクセスしてみる。
```zsh:zsh
% curl -w'\n' localhost:3000
{"status":"OK","mode":"development"}
```
オッケーオッケー。

ではXserver側には本番環境ということで別の.envファイルを置いておく。最初にgitリポジトリを作成する際，同期無視の設定ファイルである`.gitignore`を「Node」にしておくと`.env`は同期を無視するファイルとして自動的に登録されている。したがってXserver側で`git pull`しても`.env`ファイルは存在していないはずだ。（もし存在していれば同期されてしまっているので，`.gitignore`に`.env`を追加しておこう。）

プロジェクトのルートディレクトリに`.env`ファイルを新規作成し，以下を記述する。
```.env
NODE_ENV=production
```
この状態でサーバを再起動してアクセスしてみる。
![スクリーンショット 2022-03-17 13.26.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1734470/4ca2cff4-a060-01c5-68df-67043d66296f.png)
しっかりと環境変数の内容が反映されている。素敵:v:

# まとめ
ここまで，ローカルの開発環境とXserverとをgitで紐付け，Expressを用いたサーバを起動させるまでを見てきました。次の記事では認証処理まわりの実装を行っていきます。