最近、色々Node.js + Expressを試しながらWebサービスを作ってみたりしてます。その中で自分がよくつまづいた所をまとめておきます。
前提
- Node.js : v0.10.29
- Express : v4.2.0
- Vagrant
どうでもいいけど
Node.jsがv0.12リリースされてES6対応になったら、Express4がオワコンで、generator対応なKoa.jsが盛り上がりそうな気配。
つまづきポイント
なんか欲しくなったらnpmで探す
たいていの欲しい機能は、すでにnpmにモジュール登録されてます。
似たような機能がたくさんあります。なので、そのモジュールがちゃんとメンテされているか、みんな使っているくらい人気があるかを確認した方がいいです。中には、昔は王道だったけど、メンテされてなくて最新環境では動かないとか、よくあります。
あと、サンプルコードがスペルミスとかよくあります。…人のこと言えないので、あれですが。慣れないうちは、よく中身を見て使ったほうが良いかもしれないです。
Express3と4はだいぶ違う
Node.jsの有名なWebフレームワークExpressは、現時点でバージョンが4。でも、ネットとか本とかに書かれているのは、大抵バージョン3のもの。中身がかなり変わっているため、そのままのコード例だと動かないです。
まぁ、connectモジュールが分割されただけっぽいので、そこまで違いないから、3だって気がつけば問題はないかもしれない。
expressコマンドはexpress-generatorへ
expressコマンド一発で、テンプレートを生成してくれる便利な機能もExpress4からは別モジュールexpress-generator
になって提供されているので、別途インストールする必要がある。
$ sudo npm install -g express-generator
使い方自体は、さほど変わっていないはず。下記は、テンプレートエンジンをejsにしてhogeプロジェクトを作成。
$ express -e ejs hoge
モジュールのインストール方法
方法は3つ。
どこでも使いたいモジュール
$ sudo npm install hoge -g
gオプションを付けることで、グローバルな空間にモジュールをインストールしてくれる。これは、特定のプロジェクトに紐付かないようなモジュールをいれるときに使う。例えば、express-generatorやnode-dev、nodemonなどなど。
特定プロジェクトで使用する
ここで2つ方法がある。
モジュール名を指定して、npm installする方法。
$ cd ./project_dir
$ npm install hoge
プロジェクトディレクトリ直下のpackage.jsonにモジュール名を指定して、npm installする方法。基本、./node_modules ディレクトリはGit管理しないので、こちらの方法のほうがベターだと思われる。
$ cd ./project_dir
$ vi package.json
# 以下、package.jsonの中身
{
"dependencies": {
"hoge": "*"
}
}
$ npm install
モジュール間の変数の引き回し方法
これは未だにいい解決方法が思いつかないのだけど、ググってみるといろいろな方法があるみたいです。
var外しのグローバル変数宣言は、問題外としても、個人的にはクロージャパターンが良い感じがするものの…結局は、モジュール間で変数を引き回さなくてもいいように作ってしまった。
ここらへんの王道パターンが正直ないのがnode.jsの面白いところでもあり、悩ましいところでもある。
アプリ固有の定数管理
API_KEYや、なんかのパスワードや定数などをどうやって管理するか悩んだけど、config
っていうモジュールが良いらしいっぽいので、それを使っている。
$ npm install config
configディレクトリを作って、その中にdefault.jsonを作成。あとは、configモジュールを読みこめば、アクセス可能。
var conf = require('config');
console.log(conf.twitter.api_key);
{
"twitter": {
"api_key": "hogehoge"
}
}
なお、NODE_ENVをproductionにして、config/production.jsonを読み込ませて、環境に応じて定義ファイルを読み分けることも出来る。
ポート80で動かす
node.jsが気軽にWebサーバたてられるから気にしてなかったけど、ポート80番で動かすにはスーパーユーザじゃなきゃ駄目。というわけで、node.jsをそのまま80で動かすにはsudo
で起動する必要がある。
これにもいくつかのパターンがあり、rootで起動するけど、起動後に所有者を変更したり、apacheを80で動かしてproxyさせたり…。ただ、なんかどれもシンプルじゃなかったので、結局sudo
で直接node.jsを起動させている。
プロセス管理
npm start
で起動自体はできるけど、デーモン化されてないので当然シェルから抜けたら終了する…というわけで、プロセス管理するツールがいくつかある。
- supervisord
- forever
- pm2
いくつかさわってみた中で、pm2が一番高機能で使いやすかったので、こちらを使用してみた。
$ sudo npm install pm2 -g
$ cd ./project_dir
$ sudo pm2 start bin/www
# 起動スクリプトが bin/www にある例
起動
$ sudo pm2 start スクリプトファイル
監視起動
$ sudo pm2 start スクリプトファイル --watch
ファイルが更新されたら自動再起動される。開発時には必須。
終了
$ sudo pm2 stop ID
起動リスト
$ sudo pm2 list
┌──────────┬────┬─────────┬──────┬────────┬───────────┬────────┬─────────────┬───────────┐
│ App name │ id │ mode │ PID │ status │ restarted │ uptime │ memory │ watching │
├──────────┼────┼─────────┼──────┼────────┼───────────┼────────┼─────────────┼───────────┤
│ www │ 1 │ cluster │ 7265 │ online │ 4 │ 3m │ 67.027 MB │ activated │
└──────────┴────┴─────────┴──────┴────────┴───────────┴────────┴─────────────┴───────────┘
ログ閲覧
$ sudo pm2 logs
開発時にはこれを実行してデバッグする。
セッション/Cookieの使い方
セッション/Cookieには一番ハマって、今もこれでいいのかちょっと微妙な感じだけど、とりあえず動いているのでよしとする。
使用モジュール
- express-session
- connect-mongo
mongodbをセッションデータストアとして使用します。恐らくデフォルトの物理メモリ上でのデータストアだと、色々不都合がありそうです。
productionで起動した時に出る警告
Warning: connection.session() MemoryStore is not
designed for a production environment, as it will leak
memory, and will not scale past a single process.
メモリリークするよ的な。
使用準備
mongodbのインストール自体は済んでいる前提で。
$ npm install express-session
$ npm install connect-mongo
var session = require('express-session');
var MongoStore = require('connect-mongo')(session);
app.use(session({
secret: "hogehoge",
store: new MongoStore({
db: 'session',
host: 'localhost'
}),
cookie: {
httpOnly: false
}
}));
ここで注意点、cookieの有効期限を設定すると、なぜかおかしくなる…ので、設定しないようにしている(サービス上、許容できるので)。もし、ログインしっぱなしとかを実現したい場合は、もうちょっと検証が必要かも。node.js起動直後は上手く動いているんだけど、少し時間が経つとセッションがはられないという謎の挙動に出くわしたので。
vagrant環境下での自動再起動
node.jsは、テンプレートファイルや画像などのリソースファイル以外、つまりjsファイルを更新しても、即反映されない。再度、起動し直す必要があります。でも、開発時はゴリゴリコードを書いて動作確認するわけで、いちいちnpm start & ctrl+c
とか、やってられないのです。
そこにもまた便利なツールがあります。
- node-dev
- nodemon
- pm2
- supervisor
有名どころだと、node-devとnodemonかな。nodemonのほうが高機能っぽい。npmとかでインストール後は、普通にnodemon 起動スクリプト
とかでいけるんだけど、vagrant環境下で動かしていると、ホストのMac側で更新しても、それが検知されない…!
いろいろ試した中で、唯一ホスト側の更新を検知してくれるのは、pm2だけでした。
$ sudo pm2 start 起動スクリプト --watch
これで、監視モードになります。
ホストとゲストの時刻を合わせる
ただし、これだけだとうまく再起動してくれないことがあります。pm2の監視はおそらくファイルのタイムスタンプを見てやっていて、ホストPCとゲストPCの時刻がずれていると、再起動に失敗してしまいます。
vagrant側にNTP設定してもいいんですが、面倒なのでvagrantの機能で同期させます。
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
config.vm.provider :virtualbox do |vb|
vb.customize ["setextradata", :id, "VBoxInternal/Devices/VMMDev/0/Config/GetHostTimeDisabled", 0]
end
end
これで、ホストの時刻がゲストの時刻になります。
テンプレートエンジンはECT
Express4は、デフォルトのテンプレートエンジンはJadeを採用しています。Hamlっぽいやつです。
結構、高機能で簡単にかけて人気もあります。でも僕は、どうしてもHTML自体を簡易記法で記述するのに慣れなくて(Zen-CodingというかEmmetで書くのが好き)、ejsに切り替えました…。
が、なんとejsっぽいけど、ejsより速いらしいECTっていうのがイケてそうだったので、ECTを採用しています。ちなみに、Express4(3からかな?)で、ejsはレイアウト機能が分割されて、そのままでは使えないっていうのも面倒だったのでECTにしてます。
使い方
var ECT = require('ect');
var ectRenderer = ECT({ watch: true, root: __dirname + '/views', ext : '.ect' });
app.set('view engine', 'ect');
app.engine('ect', ectRenderer.render);
あとは、viewsディレクトリ以下に拡張子.ect
で保存すればOK。
記法はほんとシンプル。
<!-- エスケープ済み -->
<p><%= @hoge %></p>
<!-- エスケープしない -->
<p><%- @hoge %></p>
共通レイアウトファイルなど、ブロック機能などひと通りある。
<html>
<body>
<% content %>
</body>
</html>
面倒なのは、ifなどの分岐処理を入れた時に、正直テストしにくい…なんか文法エラーや参照できない値を見た時とか、一律同じようなエラーメッセージで落ちてしまう。ハマるとやばいです。
まとめ
初めてまともにNode.js + Expressをさわってみての感想。
- 自力で解決する気合がないとつらい
- エラーハンドリングちゃんとやらないと本番でつらい
- ググっても事例があんまりなくてつらい&古い
- 謎のエラーが出てつらい
- でも、なんか楽しい
簡単なWebアプリケーションをまともに動かすだけで、けっこう大変でした。自分のJS&Node力がなかったとはいえ、PHPやRubyとは違い調べても何にも出てこないことが多く(調べ方が悪いのか?)、試行錯誤しながらでした。
というか、調べるよりもライブラリのソース見たほうが早かったり。まぁこれは言語とかフレームワーク問わずなのかもだけど。
小さなWebアプリや非同期バンバン楽しみたいときにはNode.js採用するけど、それ以外ではまだ使いこなせてない感強い。作るのはめっちゃ楽しい。ユーザ数がもっと増えてくれると、嬉しいです。作る楽しさを失った疲弊プログラマにオススメ。