##この記事について
Node.js + Express4 + MongoDB + Vagrant(開発環境) + foreverを利用して簡単なCRUD(Create, Read, Update, Delete)アプリを開発しました。開発にあたりPHP歴3年の著者が得られた知見をまとめます。
導入編、環境編、開発編の全3回を予定しています。皆様のこれからの開発の一助になれば幸いです。
##対象読者
- ある程度の開発経験があり、Node.jsを使ったアプリケーション開発に興味がある/これから実装する人。
- ノンブロッキングI/Oを知りたい人。
- node.js + express4 + MongoDB による開発環境構築に不安がある人。
- 少しプログミングが書けてIT業界に興味があり、これからもっと学んでいきたいという意欲的な人。
##注意書き
Node.js, MongoDB, Vagrantなどのインストール方法は検索すれば該当記事がわんさか出てくるので割愛します。ただしVagrantで開発環境を作る際に流れで言及することになります。
Node.js(npm)
いまアツいJavaScript!ゼロから始めるNode.js入門〜5分で環境構築編〜
MongoDB
MongoDBのインストール
Vagrant
仮想環境構築ツール「Vagrant」で開発環境を仮想マシン上に自動作成する
chef
VagrantとChef SoloでNode.jsの開発環境を作る
##ノンブロッキングI/O『Node.js』とは
皆さんはNode.jsと聞くと「ああ、サーバサイドで動くJavaScriptね」と思われる方が多いかもしれません。
実際、JavaScriptを使ってサーバサイドが記述できますが、それよりも際立った特性は「入出力を待たせない=NonBlocking I/O」です。以下にNonBlocking I/Oの動作例を示します。
var z = 0;
while(true){
counsole.log(z);
Post.count({ id: searchId }, function(err, count) {// count = 1
console.log(count);
})
}
無限ループ内でDBに問い合わせをするというコードです。
プログラミングを多少嗜んだ事があれば、以下のような出力結果を期待するのではないでしょうか。
0
1
0
0
0
0
...
ところが実際にはこんな出力結果になります。
0
0
0
...
(延々と続く)
...
0
1
0
...
やや不可解に思えるかもしれませんが、Node.jsが処理の重いDB問い合わせを後回しにし、処理の軽いログ出力を優先的に実行しているため、このような結果になります。
この「ユーザを待たせない特性」がまさに「ノンブロッキングI/O」であり、この特性により何万もの同時アクセスを同時かつ大量に処理できるようになります。このため、Node.jsは特に同時接続が大量発生するようなAPIサーバでの利用に適していると言われています。
ただこうした仕組みがあると従来のような考え方でコードを書けない時があります。例えば以下、DBから問い合わせたjson配列の3番目に対しある処理を加えたいとします。そこで以下のように書くと失敗してしまいます。
var listNum = 3;
var num = 0;
var datas2 = [];
Post.find({}, function(err, datas){ //=> datas:[1, 2, 3, 4, 5]
Post.count({_id: datas._id}, function(err, count){ //DBに格納済みか
if(count > 0){ //格納済みのときの処理
datas.forEach(function(data){
num ++;
if (num == listNume) console.log(data);
datas2.push(data); //=> datas2:[5, 2, 3, 4, 1] !順番が違う!
})
} else {
//未格納のときの処理
}
})
})
Post.countメソッドが非同期処理であるため、countメソッドを呼び出したタイミングでノンブロッキングが発生し、datas2.pushの順序が崩れてしまいました。以下のように書けば成功するでしょう。
var listNum = 3;
var num = 0;
var datas2 = [];
Post.find({}, function(err, datas){ //=> datas:[1, 2, 3, 4, 5]
datas.forEach(function(data){
if (num == listNume) {
Post.count({_id: datas._id}, function(err, count){ //DBに格納済みか
if(count > 0){ //格納済みのときの処理
num ++;
console.log(data);
datas2.push(data); //=> datas2:[1, 2, 3, 4, 5] !合致した。!
} else {
//未格納のときの処理
}
})
}
})
})
Post.findやPost.countのようなデータベース問い合わせはNode.jsにとって「非同期処理」であり、コードを記述する際に細心の注意を払う必要があります。Node.jsはその「待たせない」という特性を維持するためにデッドロックが起こさないよう設計されているので、処理がバッディングした時に変数の値を保証しません。(処理によってはfor文のincrementの出力値がバラバラに・・・なんてことが起きる。)
##Node.jsフレームワーク『Express』とは
さてここからはWebアプリケーション開発に関する話をまとめます。Node.jsにもRubyやPython、PHPのようにWeb開発用のフレームワークがありますが、Node.jsが未だに黎明期ということもあり、これだという絶対的なフレームワークはありません。
今回はExpress4を使います。Expressはバージョンによって中身が全然違うので、他記事を参照する時はご注意ください。
Express4は、Railsのようなスカフォールドやマイグレーション、ActiveRecordを提供します。ただし、提供しているとはいえこれらの機能は最小限というべきであり、設計の大部分は開発者自身の手に委ねられています。また、Railsのような、と表現しましたが、Express4のそれらの機能はそれに比べると非常に簡素です。
試しにExpressで「SampleApp」という名前のアプリを作成してみましょう。
$ express -e SampleApp
create : SampleApp
create : SampleApp/package.json
create : SampleApp/app.js
create : SampleApp/public
create : SampleApp/public/javascripts
create : SampleApp/public/images
create : SampleApp/public/stylesheets
create : SampleApp/public/stylesheets/style.css
create : SampleApp/routes
create : SampleApp/routes/index.js
create : SampleApp/routes/users.js
create : SampleApp/views
create : SampleApp/views/index.ejs
create : SampleApp/views/error.ejs
create : SampleApp/bin
create : SampleApp/bin/www
install dependencies:
$ cd SampleApp && npm install
run the app:
$ DEBUG=SampleApp ./bin/www
アプリのフォルダとファイルがスカフォールドされました。Express4の構成は非常に簡単です。
app.js
最初に実行される。index.*のようなもの。Expressアプリの基幹部分。
package.json
バージョン管理用ファイル。npm installすると、ここが参照される。npm startで実行されるコマンドが書いてある。
routes
いわゆるコントローラ。
views
いわゆるビュー。テンプレートエンジンは複数選べるが、新たに追加する際はnpm install テンプレートエンジン名を忘れないこと。Express4のデフォルトはjade。
public
webroot。JavaScriptやCSSが入る。
bin
コマンド。 wwwでWebサーバが起動する。
詳しい実装方法は後述しますが、モデルは開発者が作成し定義します。
またnpm install {モジュール名}を行うと、node-moduleというフォルダが作られ管理されます。
npm install に-gオプションを付けることがありますが、これはグローバルインストールと言って全てのアプリでモジュールが使えるようになるという意味です。
Express - node Webフレームワーク | 日本語ドキュメンテーション
##ドキュメント指向DB『MongoDB』とは
MongoDBはいわゆるNoSQLと言われるドキュメント指向DBです。
正体はJSONライクな文字列で、joinなどの表結合の操作がない一方、データの追加・更新・削除が高速に行えるという特徴を持っています。JSONライクなのでJavaScriptと相性が良く、node.jsを利用する際は広く採用されるDBです。
操作は簡単で、まず
$ mongo
と打てばmongoDBの対話シェルが起動します。
$ mongo
MongoDB shell version: 2.6.6
connecting to: test
> show dbs
blog 0.078GB
local 0.078GB
study 0.078GB
> use study
switched to db study
> show collections
menus
posts
system.indexes
> db.posts.find();
{ "_id" : ObjectId("54b4d709856e98802ea94d5c"), "text" : "ハンバーグ弁当", "created" : ISODate("2015-01-13T08:27:53.070Z"), "pre_order_user" : [ ], "order_user" : [ ], "price" : 490, "__v" : 0 }
{ "_id" : ObjectId("54b4d715856e98802ea94d5d"), "text" : "バラエティ弁当", "created" : ISODate("2015-01-13T08:28:05.368Z"), "pre_order_user" : [ ], "order_user" : [ ], "price" : 989, "__v" : 0 }
ドキュメント指向DBは従来のSQLとは勝手が違うので、SQLに慣れ親しんだ方にとっては大変かもしれませんが、ORマッパーなど扱った事がある方であれば慣れるのも早いと思います。
表結合がないためJoinが使えませんが、upsertやフィールドに配列を指定してfindでarray_inのような検索ができるなど、便利な入出力・検索機能を備えています。
> db.test.insert({user:[‘AAA’, ‘AAABBB']}) //testというcollectionが自動で生成される
> db.test.insert({user:[‘AAABBB']})
>db.test.find({user:’AAA'})
{ "_id" : ObjectId("54bdbe3ab91dd13f1161fbb4"), "name" : [ "AAA", "AAABBB" ] }
続き: 【入門向け】Node.js + Express4 + MongoDB + Vagrant + foreverでChatWorkと連動するCRUDアプリを開発する。(2)(環境編)