herokuでNode.jsを使ってchatアプリ その3(MongoDBを利用して、メッセージを永続化)
今回はMongoDBを利用して、メッセージの永続化にまで挑戦してみました。
MongoDB自体の勉強から入ったのですが、MongoDBはすごく簡単で良いですね。
特にNode.jsとの相性が良いです。ライブラリも豊富ですし、割とドキュメントも揃っています。
今回はherokuのaddonであるmongoHQを利用します。
まずはadd-onを実行するための下記のコマンド実行してください。
$ heroku addons:add mongohq:free
これであなたのherokuアプリケーションから利用できるようになります。
※add-onを有効にするためには、herokuのユーザー検証が必要になります。
ユーザー検証にはクレジットカード番号等も必要になりますが、100MBの範囲で利用する分には無料です。
Node.jsとmongoDBをつなぐためのライブラリとして、今回はmongooseを利用します。
単純にインストールするだけであれば、下記の手順でOKです。
$ npm install mongoose
もちろんmongodbを利用するなら、installが必要になりますが、下記のサイトを見れば出来ると思います。
Quickstart - MongoDB
herokuへのdeploy用にpackage.jsonを変更します。
package.json
{ "name": "node-example", "version": "0.0.2", "dependencies": { "express": "2.2.0", "socket.io": "0.6.18", "ejs": "0.4.3", "mongoose": "1.7.4" } }
mongooseの使い方は詳しくは下記のサイトを閲覧してください。
[mongodb] - Last Verse
まずは、スキーマを定義する必要があります。
スキーマの定義方法は下記の方法です。
var mongoose = require('mongoose'); //スキーマ定義 var Schema = mongoose.Schema; var commentSchema = new Schema({ body :String, date :Date }); //uriの設定、herokuで利用するときはprocess.env.MONGOHQ_URLを利用する。 //ローカルではmongodb://[hostname]/[dbname]形式で指定する。 var uri = process.env.MONGOHQ_URL || 'mongodb://localhost/mongo_data'; //mongodbへの接続 mongoose.connect(uri); //スキーマの設定 mongoose.model('Comment', commentSchema);
この他にも属性に制約を入れたりできるらしいですが、まだ試していません。
最終的には下記のようになりました。
app.js
var express = require('express'); var app = express.createServer(); var ejs = require('ejs'); var io = require('socket.io'); var mongoose = require('mongoose'); var port = process.env.PORT || 3000; var Schema = mongoose.Schema; var commentSchema = new Schema({ body :String, date :Date }); var uri = process.env.MONGOHQ_URL || 'mongodb://localhost/mongo_data'; console.log( uri ); commentSchema.pre('init', function(next) { console.log('initialized'); next(); }); commentSchema.pre('save', function(next) { console.log('pre save.'); next(); }); app.configure(function() { var expressStatic = express.static(__dirname + '/static'); app.use(expressStatic); //mongodb://[hostname]/[dbname] mongoose.connect(uri); mongoose.model('Comment', commentSchema); }); var Comment = mongoose.model('Comment'); app.set('view engine', 'ejs'); app.set('view options', { layout: false }); app.set('transports', ['xhr-polling']); app.set('polling duration', 10); app.get('/', function(req, res) { console.log('/'); res.render('index', { locals: { port: port } }); }); app.listen(port); var socket = io.listen(app); socket.on('connection', function(client) { Comment.find(function(err, docs) { if(!err) { for (var i = 0; i < docs.length; i++ ) { console.log(docs[i].body); console.log(docs[i].date); var message = JSON.stringify({date:docs[i].date, body:docs[i].body}); client.send(message); } } }); client.on('message', function(msg) { console.log('send :' + msg); var date = new Date(); var sanitizedMsg = escapeHTML(msg); var message = JSON.stringify({date:date, body:sanitizedMsg}); client.send(message); client.broadcast(message); var comment = new Comment(); comment.body = sanitizedMsg; comment.date = date; comment.save(function(err) { if ( !err ) console.log('save.'); }); }); client.on('disconnect', function() { console.log('disconnect'); // mongooseのdisconnectってどこでするのが適切? }); }); function escapeHTML(str) { return str.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">"); } console.log('Server running at ' + port + '/');
mongooseのdisconnectをどこでするのが適切なのか
よくわかっていないので、もう少し調べているところです。
次にクライアント側にも一部変更がかかっていますが、そんなに変更はありません。
JSON形式でメッセージをやり取りするようにしただけです。
static/js/client.js
$(function() { var socket = new io.Socket(); socket.connect(); socket.on('connect', function() { console.log('connect'); }); socket.on('message', function(msg) { //JSON形式で送受信するようにしただけ。 var obj = JSON.parse(msg); $('#list').prepend($('<dt>' + obj.date + '</dt><dd>' + obj.body + '</dd>')); }); socket.on('disconnect', function(){ console.log('disconnect'); }); $('#form').submit(function() { var message = $('#message'); socket.send(message.val()); message.attr('value', ''); return false; }); });
いや〜、それにしてもmongodbはかなり簡単で良いですね。
mongoDBの勉強会とかあるなら教えてもらいたいくらいです。
割と使えそうなので、本業でも利用してみようかなぁと思うくらいです。