MyDB の紹介
Node.js Advent Calendar 18 日目の記事です。二日も遅刻してしまいました。切腹ものです。先日は hiyokur さんによる Sails.jsを使ってハマったところとか でした。
今回は MyDB の話をします。まだ調査したばかりなので間違いなどありましたらご指摘下さいませ...
MyDB とは Realtime で Reactive な Web Application を作るための Node.js 向けのライブラリです。今年初頭に LearnBoost のメンバーによって Cloudup というサービスがリリースされましたが、そのバックエンドに使われているようです。(現在は WordPress.org などを運営している Automattic 社に買収されました。)
今年の夏頃に MyDB の概要がアナウンスされ、12 月に GitHub 上でオープンソースとして公開されました。開発のリードは Socket.IO で著名な Guillermo Rauch さんです。
MyDB の概要
こんにちのウェブアプリケーションは応答性が求められます。
Ajax 通信は当たり前となり、WebSocket のようなリアルタイムでサーバと通信する技術が登場し、HTTP 2.0 ではレイテンシが小さくなるよう仕様が検討されていたり、クライアントサイドではオブジェクトの変更に対して UI がリアクティブに変更される世界観がトレンドとなりつつあります。
MyDB では、サーバ上のデータに対してクライアントから subscribe し、データの変更を購読し、サーバ側でデータを更新すると、購読しているクライアントに変更を通知する仕組みになっています。(いわゆる pub/sub)
Socket.IO との大きな違いは、ドキュメント指向であること、それゆえにデータの永続性が重視されているというところでしょうか。現在のところ MongoDB への永続化がサポートされています。
Realtime/Reactive な姿勢はどちらかと言えば Socket.IO より Meteor に似ていますが、Meteor がIsomorphic さを重視しドラスティックかつアクロバティックなアプローチを採用しているのに対して MyDB はより保守的で RESTful の考え方がベースになっているため、とっつきやすさがあると思います。
説明はこのぐらいにして、MyDB を利用したデモアプリケーションのコードを見てみましょう。
デモアプリ
ここでは変更通知を受け取る簡単なデモアプリケーションを作成します。
ミドルウェアに MongoDB と Redis を使います。Mac の場合は Homebrew で事前にインストールしておきます。
$ brew install mongodb redis $ redis-server & $ mongod &
クライアント側のライブラリは Component で書かれているため、事前に component をグローバルインストールしておきます。最新版の component ではリダイレクトするリポジトリからコードを正しく取得できない不具合があり mydb-client がインストール出来ないため、0.17.5 をインストールしておきます。
$ npm install -g [email protected]
次に、サーバ側で利用するライブラリを package.json に書いてしまいます。
package.json
{ "name": "mydb-demo", "dependencies": { "express": "~3.4.7", "mydb": "~3.1.1", "mydb-driver": "~0.5.2" } }
npm install で依存性を解決します。
次に component.json でクライアント側の依存ライブラリを書いてしまいます。
{ "name": "mydb-demo", "dependencies": { "cloudup/mydb-client": "*" } }
component install && component build で build.js を生成しておきます。
MyDB サーバは HTTP サーバと協調して動作するため、まずは定番の Express を使ってシンプルなアプリケーションを書いてみます。
app.js
var http = require('http'); var express = require('express'); var app = express(); var server = http.createServer(app); app.set('port', process.env.PORT || 3000); server.listen(app.get('port'), function() { console.log('listening on port %s', app.get('port')); });
出来ました。これに MyDB をセットアップしてみます。MongoDB のデータベース名は mydb-demo とします。
app.js
var http = require('http'); var express = require('express'); // Connect/Express 用ミドルウェア var expose = require('mydb-expose'); // DB ドライバ var db = require('mydb-driver')('localhost/mydb-demo'); var app = express(); var server = http.createServer(app); // MyDB 本体 var mydb = require('mydb')(server); app.set('port', process.env.PORT || 3000); app.use(express.json()); // ミドルウェアの登録 app.use(expose({ mongo: 'localhost/mydb-demo', url: function() { return 'http://localhost:' + app.get('port'); } })); app.use(express.static(__dirname + '/build')); app.use(express.static(__dirname + '/public'));
それぞれのライブラリの役割は以下のとおりです。
mydb
MyDB 本体。
ドライバでのデータの変更とクライアントの通知の橋渡しを行う。
クライアントとは Engine.IO を使い push 通知を行う。
mydb-expose
MyDB と Connect/Express を組み合わせて使うためのミドルウェア。
mydb-driver
MongoDB のドライバ。
API は monk 互換。
データ更新時に Redis にて MyDB へ通知 (publish) する。
簡単のため事前にデータを入力しておきます。
$ echo "db.users.insert({ name: 'nulltask', url: 'http://null.ly', interest: 'Node.js' })" | mongo mydb-demo
次はサーバ側に、データを読み出す部分とデータを更新する部分を実装してみます。
var db = require('mydb')(); // users コレクションのハンドラを取得 var users = db.get('users'); // データ取得 app.get('/users/nulltask', function(req, res) { var doc = users.findOne({ name: 'nulltask' }); res.send(doc); }); // データ更新 app.post('/users/nulltask', function(req, res) { var update = req.body; var doc = users.findOne({ name: 'nulltask' }); delete update.name; // name を上書きしないよう念のため doc.success(function(doc) { res.send(users.update(doc._id, { $set: update })); }); });
monk や mongoose を利用したことのある人なら今までのイディオムと殆ど変わらないことがわかるかと思います。
次にクライアント側のコードを実装してみます。
var mydb = require('mydb')(); // mydb.get に HTTP 側で GET するための URL を指定し変更を subscribe する var user = mydb.get('/users/nulltask', function() { // subscribe 後に実行されるコールバック // ドキュメントのデータが user オブジェクト経由でアクセスできる console.log(user); user.on('url', function(val) { console.log(val); // 変更後の url の値 console.log(user.url); // 自動的に user オブジェクトも更新される }); user.on('interest', function(val) { console.log(val); // 変更後の interest の値 console.log(user.interest); // 自動的に user オブジェクトも更新される }); });
MyDB を require した時点で Engine.IO を介してサーバとの通信が確立されます。mydb.get でデータの変更を subscribe し、user.on('プロパティ名') でデータの変更をハンドリングします。
初回のデータ取得は HTTP 経由で行われますが、データの更新は Engine.IO 経由でサーバから push されます。
curl などを使ってサーバにデータを POST してみるとブラウザ上で変更が通知されるかと思います。
$ curl -v -H "Accept: application/json" -H "Content-type: application/json" -X POST -d '{ "url": "http://twitter.com/nulltask" }' http://localhost:3000/users/nulltask $ curl -v -H "Accept: application/json" -H "Content-type: application/json" -X POST -d '{ "interest": "AWS" }' http://localhost:3000/users/nulltask
以上、駆け足となってしまいましたが MyDB の紹介を終わります。テストコードを読むと他にもさまざまな機能がありそうですので引き続き調査していこうと思います。
Node.js Advent Calendar 19 日目の記事は takeshy さんによる Socket.IO用 フレームワーク socket.io-reqev です。
リンク
The Need for Speed


















