在Node.js模块函数之间传递数据库上下文

我正在Node.js上运行Express,想知道如何在不同的Node模块之间有效地传递单个数据库连接上下文对象(想想它们就像应用程序模型一样)。

我想这样做是为了能够在一个模型中启动一个数据库事务,并在一个HTTP请求的持续时间内保持它对其他受影响模型的调用。

我已经看到有人试图在我的路由运行之前使用作为中间件暴露的每个请求数据库连接来解决这个问题(从连接池中获取数据,然后在路由之后运行另一个中间件以将连接返回到池)。 不幸的是,这意味着明确地将一个上下文对象传递给所有不受欢迎的函数,而这些函数不雅,笨拙。

我还看到有人在谈论延续本地存储和AsyncWrap模块,但我不清楚他们如何解决我的特定问题。 我试着用continuation-local-storage简单地工作,但是因为我主要在代码中使用promise和generator,所以它不能从run方法返回状态(它只是返回传递给它的callback的context对象)。

以下是我想要做的一个例子:

 // player-routes.js router.post('/items/upgrade', wrap(function *(req, res) { const result = yield playerItem.upgrade(req.body.itemId); res.json(); }); // player-item.js const playerItem = { upgrade: Promise.coroutine(function *(user, itemId) { return db.withTransaction(function *(conn) { yield db.queryAsync('UPDATE player_items SET level = level + 1 WHERE id = ?', [itemId]); yield player.update(user); return true; }); }) }; module.exports = playerItem; // player.js const player = { update(user) { return db.queryAsync('UPDATE players SET last_updated_at = NOW() WHERE id = ?', [user.id]); }) }; module.exports = player; // db.js db.withTransaction = function(genFn) { return Promise.using(getTransactionConnection(), conn => { return conn.beginTransactionAsync().then(() => { const cr = Promise.coroutine(genFn); return Promise .try(() => cr(conn)) .then(res => { return conn.commitAsync().thenReturn(res); }, err => { return conn.rollbackAsync() .then(() => logger.info('Transaction successfully rolled back')) .catch(e => logger.error(e)) .throw(err); }); }); }); }; 

这里有几个注释:

  1. wrap函数只是一小块包装中间件,允许我在我的路线中使用生成器/产量。
  2. db模块也是stream行的mysql模块的一个小包装,这已经promisified。

我想要做的,可能在db.queryAsync ,检查是否有一个conn对象设置在当前上下文(我设置了在db.withTransaction return Promise...调用)。 如果是这样,请使用该连接执行所有后续数据库调用,直到上下文超出范围。

不幸的是,在CLS命名空间代码中包装return Promise...调用不允许我实际返回promise – 它只是返回了context对象,这在我的情况中是不正确的。 它看起来像CLS的大多数用法依赖于实际上不从runcallback内部返回任何东西。 我也看着cls-bluebird,但是似乎没有做我所需要的。

有任何想法吗? 我觉得我很接近,但这并不完全符合我的需要。