前編では、Sequelizeをインスタンス化するために自前でdbconnモジュールを作って、app.jsはこんな感じにシンプルに書けるようになった。
var express = require('express')
, resource = require('express-resource')
, config = require('config')
, sequelize = require('dbconn')(config)
, app = module.exports = express.createServer();
app.resource('users', require('./routes/users'), { id: 'id' });
実際にSequelizeを使ってDBに対するCRUD操作を行うAPIの実装を書く以前に、まだDBすら存在しないのでmysqlコマンドでDBだけ作っておく。
$ mysql -u root -p mysql> create database test; mysql> grant all privileges on test.* to 'h2plus'@'%' identified by 'password'; mysql> \q
次にテーブルの定義をするわけだが、もうSQL文を直接書くことはない。Sequelizeのimportメソッドを使えば、モジュールとして実装されたモデルを定義することができる。サンプルとして、以下のSQLで定義されるユーザーテーブルを作ってみよう。
create table users
(
username varchar(255),
password char(40)
);
対応するモデル定義のモジュールはこうなる。
module.exports = function(sequelize, DataTypes) {
return sequelize.define('user', {
username: DataTypes.STRING
, password: 'char(40)'
});
};
これをmodels/user.jsというファイルに保存しておく。sequelize.define()の第一引数に指定しているモデル名は単数形になっているが、Sequelizeが実際に定義するテーブル名は複数形になる。
いま定義したUserモデルを動的に読み込むmodelsモジュールをlibs/models/index.jsに実装していく。libsディレクトリは前編でdbconnモジュールを実装するときに作ったものだ。
module.exports = function(sequelize) {
var fs = require('fs')
, path = require('path')
, directory = 'models'
, models = exports;
try {
fs.lstatSync(directory);
}
catch(e) {
fs.mkdirSync(directory);
}
models.__cache = {};
fs.readdirSync(directory).forEach(function(fname) {
if(/\.js$/.test(fname)) {
var name = path.basename(fname, '.js')
, realpath = fs.realpathSync(directory+'/'+fname);
models.__defineGetter__(name, function() {
if(models.__cache[name] === undefined) {
models.__cache[name] = sequelize.import(realpath);
}
return models.__cache[name];
});
}
});
return models;
};
これで、modelsディレクトリにテーブル定義モジュールを作るだけで、アプリケーションにモデルを追加することができるようになる。アプリケーションで定義したモデルをDBに反映させるにはSequelizeのsyncメソッドを使う。アプリ開発中にモデルが更新されることは多いだろうから、syncメソッドの呼び出し自体をAPI化してしておくと便利だ。
syncメソッドを実装したAPIを用意する前に、今までapp.jsに書いてあったexpress-resourceを使ったコードや、Sequelizeを使うコードをroutes/index.jsにカプセル化してしまおう。app.jsの最終形はこうなる。
var express = require('express')
, resource = require('express-resource')
, config = require('config')
, app = module.exports = express.createServer();
//
// Configuration
//
app.configure(function() {
app.set('views', __dirname + '/views');
app.set('view engine', 'ejs');
app.use(express.bodyParser());
app.use(express.cookieParser());
app.use(express.methodOverride());
app.use(app.router);
app.use(express.static(__dirname + '/public'));
});
app.configure('development', function() {
app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
});
app.configure('production', function() {
app.use(express.errorHandler());
});
//
// Routes
//
require('./routes')(app, config);
//
// Launch
//
app.listen(process.env.PORT || 3000, function() {
console.log("Express server listening on port %d in %s mode", app.address().port, app.settings.env);
});
冒頭に紹介したコードでは、Sequelizeをapp.js内でインスタンス化していたが、実際にSequelize(モデル)を使うのはroutesの中になるので、Sequelizeのインスタンス化に必要なconfigオブジェクトをroutesに渡すようにしている。configオブジェクト自体はアプリケーションの設定を保持するものであり、Sequelize以外のモジュールが必要とする設定が含まれることを考えると、app.js内でインスタンス化するのが適切だろう。
30行目でまだ存在しないroutesモジュールを呼び出すコードを先に書いてしまったが、routesモジュールはroutes/index.jsで以下のように実装している。
module.exports = function(app, config) {
var sequelize = require('dbconn')(config)
, models = require('models')(sequelize);
app.resource('users', require('./users'), { id: 'id' });
};
これでしばらくapp.jsにコーディングすることはなくなるだろう。アプリケーションロジックのメインはroutesモジュールが請け負うことになる。
結局三部作に(笑)後編に続く…
2 コメント