Node.js の ActiveRecord ライブラリ Knex.jsを使う
今日の知見です〜。
Node.js からActiveRecordを用いてRDBMSにアクセスするに辺り、 knex.jsが便利でした。
var knex = require('knex')({ client: 'mysql', connection: { host : '127.0.0.1', user : 'your_database_user', password : 'your_database_password', database : 'myapp_test' }, pool: { min: 0, max: 7 } }); knex .select('*') .from('test') .where('id', id) .then(function(rows) { console.log(rows); }) .catch(function(err) { console.log(err); });
雑にサンプル書いたけど、コネクションプーリングをknex側でやってくれるのが良い。 インターフェイスはcallbackでもPromiseでも選べる(上のサンプルはPromiseで書いてる)
selectを2回発行するときとか
javascriptの非同期に慣れていないとよくやってしまう失敗だが、 1回目のselect の結果を受けて2回目のselect を発行するとき、
var user_id; knex .select('user_id') .from('user') .where('id', id) .then(function(rows) { user_id = rows[0].user_id; }) .catch(function(err) { console.log(err); }); knex .select('*') .from('detail') .where('user_id', user_id) .then(function(rows) { console.log(rows); }) .catch(function(err) { console.log(err); });
user_id を取得してdetail テーブルからデータを引くコードだが、上記のコードはうまく動かない。 2つのselect 文が非同期に実行されるので、user_id を取得してから detail に select される保証はない。
次のように1つめのselect文のコールバックの中で2つめのselect文を呼べば select文の発行順番が保証される。
var user_id; knex .select('user_id') .from('user') .where('id', id) .then(function(rows) { user_id = rows[0].user_id; knex .select('*') .from('detail') .where('user_id', user_id) .then(function(rows) { console.log(rows); }) .catch(function(err) { console.log(err); }); }) .catch(function(err) { console.log(err); });
コールバックがネストするので非常にコードが読みにくい。select文を発行するたびに ネストしないといけないのか?Promise はこうした callback地獄を解決するための手段だ。
var user_id; knex .select('user_id') .from('user') .where('id', id) .then(function(rows) { user_id = rows[0].user_id; return knex .select('*') .from('detail') .where('user_id', user_id) }) .then(function(rows) { console.log(rows); }) .catch(function(err) { console.log(err); });
1つめのthen で user テーブルへのselectが実行され、2つめのthen で detail テーブルへのselect が実行される。 両者のselect 文のどちらかでエラーが発生すれば最後のcatch でエラーが拾われる。 わかりやすい。
DBへの接続をシングルトンで保持する。
複数のライブラリからknexを呼ぶ場合、各ライブラリでknexを初期化すると その文だけDBへのコネクションが増えるので、knex インスタンスを1つに保持しつつ、 複数のライブラリからknex を使用したい。 そのような場合、シングルトンなクラスを作る必要がある。
Node.js の require の仕組みはシングルトンを非常に作りやすい。
// knex-singleton.js var knex = require('knex')({ client: 'mysql', connection: { host : '127.0.0.1', user : 'your_database_user', password : 'your_database_password', database : 'myapp_test' }, pool: { min: 0, max: 7 } }); module.exports = knex;
このようなモジュールを作っておき、各ライブラリから
var knex = require('./knex-singleton'); knex.select('*').from('user').then(function(rows){ console.log(rows); });
こんな感じで使える。knex-singleton が何回呼び出されようが、knex インスタンスは常に1つになる。