基礎から学ぶNode.js

第4回Node.jsアプリケーションとMongoDBの連携

はじめに

前回から、いよいよNode.jsを使ったWebアプリケーションを作成しはじめました。前回はデータを保存および読み出す処理がなかったので、今回はデータベースとの連携を実装したいと思います。使うのはMongoDBです。リレーショナルデータベースに対して、MongoDBはドキュメント指向データベースという位置づけです。JSON構造をそのまま保存できるため、Node.jsとの親和性も高いです。簡単に導入でき、日本語ドキュメントもあるので、初めてでもすんなり使えると思います。

MongoDBのセットアップ

MongoDBのサイトから、OSごとに用意されたアーカイブをダウンロードして展開してください。展開したフォルダの直下にあるbinディレクトリ内にコマンド群があるため、これらを使って操作します。サーバデーモンとなるのがmongodで、クライアントとなるのがmongoになります。

まず適当にデータファイルを置く空ディレクトリを用意してください。そこを指定してmongodを実行するだけサーバが起動します。

mongod --nojournal --noprealloc --dbpath <データディレクトリパス>

なお、MongoDBは基本的に最初から巨大なファイルを用意することで、パフォーマンスを上げる方針で作られているため、デフォルトで起動すると、何もデータを入れてなくてもGB単位でディスク領域を占有します。今回は単に開発で使用するので、ジャーナル機構やデータファイルを先に確保する機構は、無効化しています。フォアグランドで起動するので、CTRL + Cで終了できます。

Mongooseを使ってNode.jsからMongoDBを利用

MongoDBをnodeから利用するには、node-mongodb-nativeという標準ドライバモジュールがあります。今回はそれをさらにO/Rマッパーのように利用できるモジュールMongooseを使って、MongoDBを使ってみます。

前回作成したfirstappのディレクトリで関連モジュールをインストールします。

cd firstapp
npm install mongoose

として利用するモジュールをインストールします。

モデルの実装

続いてmodel.jsというファイル名で、下記のコードを作成定義します。

var mongoose = require('mongoose');
var db = mongoose.connect('mongodb://localhost/firstapp');

function validator(v) {
  return v.length > 0;
}

var Post = new mongoose.Schema({
    text   : { type: String, validate: [validator, "Empty Error"] }
  , created: { type: Date, default: Date.now }
});

exports.Post = db.model('Post', Post);

まず、mongooseモジュールの読み込みです。次にconnectメソッドで、localhostで稼働するMongoDBのfirstappデータベースに接続します。

投稿データをPostというモデルで定義します。Postモデルのスキーマは、textという文字列フィールドと、dateという日付型でデフォルトは現在日時が入るフィールドを定義します。またtextフィールドには、⁠文字列長が0より大きいか」バリデート関数を指定しています。

modelメソッドは、⁠Post」という名前でPostモデルのスキーマを登録しています。MongoDB上では、⁠posts」というコレクション(RDBでいうテーブルに該当)で管理されます。Mongooseによって、小文字かつ複数形に変換された名前となります。戻り値がデータアクセスモデルとなるので、それをexportsに渡して、外から使えるようにします。

アプリケーションの修正

前回作成したroutes/main.jsを修正して、Postモデルを利用するようにします。

var model = require('../model');
var Post = model.Post;

exports.index = function(req, res){
  Post.find({}, function(err, items){
      res.render('index', { title: 'Entry List', items: items })
    });
};

exports.form = function(req, res){
  res.render('form', { title: 'New Entry' })
};

exports.create = function(req, res){
  var newPost = new Post(req.body);
  newPost.save(function(err){
     if (err) {
       console.log(err);
       res.redirect('back');
     } else {
       res.redirect('/');
     }
    });
};

はじめに、model.jsを読み込みます。さらにPostモデルを直接利用できるように変数宣言しなおします。

ルートパスにアクセスしたときのリスト表示では、findメソッドを使って、postsコレクションのドキュメントを全て取得しコールバック関数function(err, items)で、結果を処理します。Findメソッドの第1引数は条件(SQLでいうwhere句)を指定できます。この場合全てなので空のオブジェクトにしています。コールバックの方は、第1引数にクエリ失敗時のerrオブジェクト、第2引数に正常時の結果が入ります。よって、errオブジェクトの有無で失敗と成功の切り分けをします。

/createに対する処理は、まずリクエストbodyの中身をそのままPostモデルのコンストラクタに渡して、一致するフィールド(ここではtext)の値をセットしつつ生成しています。そしてsaveメソッドを呼んでコレクションに書き込んでいます。コールバック関数function(err)で、成功か失敗かを判断し、エラー時は前の画面に戻しています。前の画面のURLは、Referrerヘッダを利用してExpressが自動で処理しています。設定した文字列長確認のバリデートに不適合だった場合もここでエラーとなります。

動作確認

以上で実装は完了です。アプリケーションを起動して、

node app.js

ブラウザから「http://localhost:3000/」にアクセスしてみましょう。

「New Entry」リンクからフォームにテキストを入力してPostしていくと、どんどんリストの項目が増えていくことがわかると思います。テキストを空でPostすると、コンソールに出力されるログでバリデートエラーが確認できます。

最後にMongoDBのシェルから実際のデータを確認してみます。mongodコマンドと同じ場所にあるmongoコマンド別のプロンプトから実行してください。まずshow dbsと命令すると、存在するデータベースの一覧が出ます。⁠use <データベース名>」で操作するデータベースを指定します。さらに「show collections」で、データベース内のコレクションの一覧が出ます。⁠db.<コレクション名>.find()」でコレクションの中身を表示します。

$ mongo
MongoDB shell version: 2.0.6
connecting to: test
> show dbs
firstapp	0.078125GB
local	(empty)
> use firstapp
switched to db firstapp
> show collections
posts
system.indexes
> db.posts.find()
{ "text" : "最初の投稿です。", "_id" : ObjectId("4ff96c7d7f12ede516000001"), "created" : ISODate("2012-07-08T11:18:21.466Z") }
{ "text" : "2番目の投稿です。", "_id" : ObjectId("4ff96c8b7f12ede516000003"), "created" : ISODate("2012-07-08T11:18:35.904Z") }
{ "text" : "3番目の投稿です。", "_id" : ObjectId("4ff96ca37f12ede516000006"), "created" : ISODate("2012-07-08T11:18:59.385Z") }

_idというのは、MongoDBのドキュメントにおける主キーのようなもので、未指定だと自動で割り当てられます。また特にセットしていない作成日時createdもスキーマに定義したとおり、追加時点での現在日時が入っています。

最後に

今回は、Mongooseを使ってMongoDBに、前回のアプリケーションを接続してみました。シンプルではありますが、非常に少ないコードで実装が終わってしまったと思います。この手軽さがNode.jsの魅力の1つだと筆者は思っています。次回以降は、様々なクラウドサービス上でこのアプリケーションをデプロイして動かしていきたいと思います。

おすすめ記事

記事・ニュース一覧