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制限がかけられているため,フロントエンドから直接APIを叩くことが出来ません。また,そもそもこれではFileMaker Data APIにアクセスするための認証データをフロントエンド側に保持させる必要があり,セキュリティ上問題が残ります。そこで

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

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

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

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

目的

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

開発環境

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

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

また,ローカル開発環境ではGitの初期設定が終わっていてGitHub Desktopがインストールされている前提です。

Step 1. ローカルにgitリポジトリを作成し,Xserver側でpull出来るようにする

1-1. ローカルにgitリポジトリを作ってリモートに紐付け

GitHub DesktopでNew Repository...を選べばローカルでフォルダ作ってgit initからリモートリポジトリ作成までやってくれる。.gitignoreNodeを選んでおこう。作成したらひとまずPublish repositoryでGitHubへアップ。

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

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

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

~/.sshディレクトリに移動して以下:

xserver ssh
$ ssh-keygen -t rsa -b 4096 -C "[email protected]"

最後のはコメントなので適当でOK。途中プロンプトでファイル名やパスフレーズが聞かれるが,これも入力せずEnterでOK。作成されたid_rsa.pubファイルをviか何かで開いて内容をコピーし,GitHubの"Settings" > "SSH and GPG keys" > "SSH keys" > "New SSH key"から登録しておく。

先に作成したリモートリポジトリのページに入り,"Code"ボタンからSSHクローン用の文字列を取得。

リポジトリを格納したいディレクトリの中で(今回はpublic_htmlと同階層にnodeというディレクトリを作成した),以下を実行する。

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ファイルを作ってみる。

test.js
console.log("Hello, World!")

作成したファイルをリモートにpushしてから,Xserver側でpull。

xserver ssh
$ git pull
$ node test
Hello,world!

うまくいったっぽい。

Step 2. とりあえずExpressサーバを立ててみる

Node.jsのwebフレームワークとしてはExpressを使用することとした。Expressで超シンプルなサーバを立てて,Webからアクセス出来るようにしてみる。

2-1. サーバ作るよ

とりま,Expressの公式Hello Worldをそのまま使ってみる。

zsh
% npm init
% npm install express --save
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を保存してから

zsh
$ node server
Example app listening on port 3000

ここでとりあえずターミナルからcurlでlocalhost:3000を叩いてみると

zsh
% curl -w'\n' localhost:3000
Hello World!

うん,出来てるね。

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

Xserverにsshでログインして

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により自動的に追加された設定が書き込まれているので,そちらではなく上の方に記述すること。

.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の詳しい内容は↓このあたりを参照。

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

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

成功。

2-2. Daemon化するよ。

Node.jsで立てたサーバはそのままだとエラーで落ちてしまうので,自動で再起動できるようDaemon化する。Daemon化にはforeverを使うことが多いようだけど,foreverのnpmを見ると「新規にインスコするならpm2nodemon使いな〜」と書いてある。

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
$ npm install pm2 -g
$ npm install nodemon -g

pm2の解説見るとsudoでインストールしていることが多いけど,Xserverでsudoを付けずにインストールしても問題無かった。

インストールされたら

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のスクリプトも変えておく。

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
$ npm install dotenv --save

プロジェクトのルートディレクトリに.envファイルを作成。とりあえずこんな内容で。

.env(開発環境用)
NODE_ENV=development

server.jsをちょっといじって,/にアクセスされたらOKステータスと環境を返すようにしてみる。

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
% 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

この状態でサーバを再起動してアクセスしてみる。

しっかりと環境変数の内容が反映されている。素敵

まとめ

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