正月休みにnode.jsの勉強をしたメモ
前知識としては nodeは非同期で async/await使うと便利というぐらい
#作るもの
スマートフォンゲーム向けのAPIサーバを題材にした
大雑把な設計は下記
WindowsでもMac、Linuxでも動くようにする
開発は Windows10 Proと Mac Mojave で開発する
Node.js、Express、MongoDB、Redis、MySQL
を使う
RedisはMongoのAPIキャッシュに使う
MySQLは 課金関連などのトランザクションがシビアな部分に使うかも
#設計
開発を早く始めたかったので適当にした
ユーザテーブルは1つで良いが、マイクロサービス、DBレスポンスなど考え 機能ごとに分割した
マスタテーブルは当然複数分割
APIは user_idごとにシャーディングしやすいよう、パスパラメータの最初の方にuser_idを入れてみる
この辺は パッと思いついた設計なので変わるかもしれない
#暗号化等
とりあえずJWTを使う事にする
デバイスIDにより新規登録を行った時 user_idとpasswordが発行される
ランダムパスワードなのでHash化しなくていいが勉強のためHash化して保存する
user_idとpasswordでログインしたら 数時間有効なSessionが発行されRedisにキャッシュされる
以降の通信には user_idとsession_idを送り認証する
サンプルなのでこのぐらいでいいはず
#環境構築
Dockerにする。DB等もたくさん使うので DockerComposeにする
##Windows
Windows10 Proなので Docker for Windowsにする
WSLでBashを使いつつ、パス問題があるので Dockerは今回は Gitbadhにした。使い分け便利!
##Mac
Docker for Mac
特に変な設定は行わない
##DockerCompose
小さいイメージにしたいのでAlpineで
node、Redis、Mongo、MySQLの4つを建てる
まずは通常の設定をし起動するようにする
#サンプルプロジェクト
私のいつもの勉強法は
Githubで該当する条件 今回はNode.js で検索し
スター上位から 参考に適したプロジェクトを探す
今回は
https://github.com/linnovate/mean
を参考にしてみた
Gitlab.comでソース管理を行なう
#HelloWorld
express等をnpmでインストールする
routerを使い / へのアクセスで HelloWorldを返すようにした
#DevOps
面倒くさいが最初にやるべきなのは 作業工数が下がる環境構築
ってことでまず nodemonによる自動ビルドを行った
裏でファイルを変更したら 即時にnodeを立ち上げ直してくれる
SlackBotを導入し Slackにコミット、Issue等を送るように
APIのテストのために PostManを使い、テストデータを作る
#DB作成
MongoDBのラッパーにMongooseを使う
メインとなる5テーブルほど定義を作る
MongoDBでコマンドラインからDBを操るテスト
そして DB初期値で数人のユーザデータを作る
Require関連があるのかー 設定する
#CRUDするAPI作成
APIを何個か作ってテストする
body-parserなどを使ってJSONパースする
#リファクタリング
フォルダ構成などを見直し MVC的な構成にする
ModelsにDBスキーマ等を記述
ControllersにデータのValidateや変形を行い
Routesにルーティング
コントローラーが太ってるけど とりあえず後で考えよう
コールバックをやめ、async/awaitにした。
また、Routeを渡したときに Requestの中身が消えて消耗したが
const router = express.Router( { mergeParams: true });
と、mergePramasを設定すれば Requestを引き継げた すばらしい
#Webpackで消耗する
Typescript化を考えたので Webpackの導入を考える
が ちょっと消耗したので、Webpackあきらめる
#Typescript導入
Webpackを使わずに導入した
今はWebpack使わなくても concurrentlyを使えば tscをwatch出来た
JSを1つにまとめることは出来ないが必須ではない
Dockerの構成を見直したり 少し苦労したが Typescriptで動くようになった
##Typescript化
Typescriptにしても JavaScriptの文法が使えるが
せっかくなので Typescriptにかえてみる
TypeScriptは基本的には 型付けやClass等が使えるようになっている
その JavaScriptライブラリの型付けのライブラリ@typesもインストールする
export default等 色々とややこしい
#Validation
APIのデータのValidationに Joiを使ってみる
これで APIの入力JSONに 必須項目や型が設定出来、不要なデータがあるとエラーで落とせるようになる
設定ファイル等にもいちおうValidationを導入した
おそらく最終的には 設定ファイルは環境変数なりファイルなりから読む事になるだろうから
#node gyp
次のパスワード暗号化に bcryptを使う事にしたが
npmのinstallでエラーがおきる
原因を調べたところ、alpineには Pythonやg++等がないため node gypがビルド出来てない
ので 色々と消耗した
結論からいえば dockerfileにて node gyp環境をつくればいいようだ
# node-gyp使うためPythonとc++必要
RUN apk --no-cache add --virtual native-deps \
g++ gcc libgcc libstdc++ linux-headers make python && \
npm install --quiet node-gyp -g &&\
npm install --quiet
# apk del native-deps
こんなかんじで 入れると大丈夫のようだ
いちおう 怖いので delはコメントにしてるが、後に無駄は全部排除したい
これで bcryptがインストールできるようになった
#パスワード暗号化
パスワードを生成し bcryptで暗号化し、DBに保存する
何度もいうが 今回はパスワードは自動生成なので 暗号化しなくてもいいんだけどね・・
passportでちゃんとBasic認証出来るところまで。
APIサーバなのでセッションはfalseに(同じサーバになるとはかぎらない)
#ちゃんと暗号化考える
JWTで行なう方針で問題ないことを確認
JWTに対する知見をゲット
passportと組み合わせ、JWTのトークンを生成し
クライアントのAuthorizedヘッダにトークンを渡せば 承認ができるはず
で 一通りの設計やテストコードを成功させる
#APIやテーブルのリファクタリング
今まではAPIエントリポイントは 適当な設計だったが
マイクロサービス化を考え、最初にサブシステム名≒テーブル名 にし、振り分けやすくし
その後に {user_id}をつけ、シャーディング時に振り分けやすく設計の見直し
以上が三ヶ日にできたこと
#インストールしたnpm
##types
"@types/bcrypt": "^3.0.0",
"@types/express": "^4.16.0",
"@types/joi": "^14.0.1",
"@types/mongoose": "^5.3.5",
"@types/node": "^10.12.18",
##ビルド、自動起動
"typescript": "^3.2.2",
"node-gyp": "^3.8.0",
"nodemon": "^1.18.9",
"concurrently": "^4.1.0",
##webAPI関連
"express": "^4.16.4",
"express-async-handler": "^1.1.4",
"body-parser": "^1.18.3",
"compression": "^1.7.3",
"cookie-parser": "^1.4.3",
"helmet": "^3.15.0",
"method-override": "^3.0.0",
##認証、暗号、バリデーション
"bcrypt": "^3.0.3",
"joi": "^14.3.1",
"mongoose": "^5.4.1",
"passport": "^0.4.0",
"passport-jwt": "^4.0.0",
"passport-local": "^1.0.0",
##その他
"winston": "^3.1.0"
"moment": "^2.23.0",
"moment-timezone": "^0.5.23",
#今後やること
認証(実際は許可)でJWTを実際に使う
URL、関数名のリファクタリング
Request内容のValidate(現在はDB更新時のValidateのみ)
Responceをちゃんと返す(エラーコード作る)
作ってないAPI、DB
課金関連
Push通知等のサービス
エラー処理
テストフレームワーク
CI、ChatOpsでの自動ビルドパイプライン
#参考にしたサイト
https://github.com/linnovate/mean
https://qiita.com/nkjm/items/723990c518acfee6e473
http://hyata-dev.hateblo.jp/entry/2016/03/11/133632
https://www.ikemo3.com/inverted/typescript/javascript-to-typescript/
https://qiita.com/zuzuwen/items/2577680a5432f5b15247
https://qiita.com/papi_tokei/items/9b852774114ebc7a6255
https://knimon-software.github.io/www.passportjs.org/guide/
https://www.djamware.com/post/58eba06380aca72673af8500/node-express-mongoose-and-passportjs-rest-api-authentication
など 数々のサイトありがとうございました