PG-Promise提供了一个很好的安全事务包装器
db.tx(t => {
// Here you can use t to run any queries
// in a single transaction, on a single connection.
})
Apollo GraphQL Express提供了一个处理GraphQL请求的Express中间件。它取决于一组预定义的"解析器"它解决了请求的不同部分。这些解析器从库中调用,并将它们的结果放在一起形成响应。
我希望能够在pg-promise事务中包装一个GraphQL请求,该请求可以获取多个数据或执行多个数据库操作(突变),从而实现基本的事务完整性。没有明显的方法可以做到这一点。
我提出的最好的想法是创建我自己的中间件,在GraphQL之前运行,这会向请求对象添加一个新的事务任务:
const bindTx = (req, res, next) => {
db.tx(async (t) => {
req = Object.assign(req, {getTransaction: () => t})
await next()
})
}
我的想法是,这个中间件将确保下一个中间件在事务中运行。我把它放在GraphQL之前的Express链中:
app.use('/graphql', bodyParser.json(), bindTx, graphql)
我的graphql
设置然后通过bindTx
将请求添加到请求中,并通过context
将其传递给解析器:
const graphql = graphqlExpress((req) => {
const t = req.getTransaction()
return {
schema: makeExecutableSchema({
typeDefs,
resolvers
}),
context: {t}
}
}
最后,解析器可能如下所示:
getSomethingById(_, {somethingId}, context) {
context.t.one(
'select * from something where id = ${somethingId}',
{somethingId}
)
}
由于我理解GraphQL按顺序运行解析器,并且它们可以返回一个必须在下一个解析器运行之前解析的promise,我认为这样可行。
这似乎导致竞争条件,大多数情况下它会产生错误Unhandled rejection Error: Querying against a released or lost connection.
我认为这意味着当解析器运行并尝试查询时,事务已经关闭。
我根本不依赖于这种方法,事实上它似乎过于复杂,但我正在寻找一些方法来使我的GraphQL解析在一个事务中运行。虽然我目前正在使用Express,Apollo和PG-Promise,但如果其他库可以解决问题,这些都不是要求。
有什么想法吗?