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 предоставляет промежуточное программное обеспечение Express, которое обрабатывает запросы GraphQL. Это зависит от набора предопределенных «преобразователей», которые разрешают различные части запроса. Эти преобразователи вызываются из библиотеки, и их результаты объединяются для формирования ответа.
Я хотел бы иметь возможность обернуть запрос GraphQL, который извлекает несколько фрагментов данных или выполняет несколько операций (мутаций) с базой данных, в транзакцию pg-prom, чтобы обеспечить базовую целостность транзакции. Для меня нет очевидного способа сделать это.
Лучшая идея, которую я придумал, - это создать собственное промежуточное ПО для запуска до GraphQL, которое добавляет новую задачу транзакции к объекту запроса:
const bindTx = (req, res, next) => {
db.tx(async (t) => {
req = Object.assign(req, {getTransaction: () => t})
await next()
})
}
Я считаю, что это промежуточное программное обеспечение будет обеспечивать выполнение следующего промежуточного программного обеспечения внутри транзакции. Я помещаю это в цепочку Express перед GraphQL:
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 запускает преобразователи последовательно, и они могут возвращать обещание, которое должно быть выполнено до запуска следующего преобразователя, я подумал, что это сработает.
Похоже, это приводит к состоянию гонки, при котором в большинстве случаев возникает ошибка Unhandled rejection Error: Querying against a released or lost connection.. Я считаю, что это означает, что транзакция уже была закрыта, когда преобразователи работали и пытались выполнить запрос.
Я совершенно не привязан к этому подходу, на самом деле он кажется чрезмерно сложным, но я ищу способ заставить мое разрешение GraphQL работать внутри транзакции. Хотя в настоящее время я использую Express, Apollo и PG-Promise, это не требования, если другие библиотеки могут решить проблему.
Любые идеи?
Тело транзакции ожидает внутри себя необязательного обещания, что означает, что вы можете связать любую функциональность внутри транзакции, пока она выполняется. Вы даже можете выполнять сложное секвенирование, как в этот пример.
@Herku, спасибо, это фантастическое предложение. Я никак не привязан к Express, это было просто по умолчанию. Как только у меня будет время вернуться к этому, я попробую Коа. Я немного отредактировал вопрос, чтобы прояснить, что инструменты не обязательны.
@ vitaly-t, PG-Promise меня пока что впечатлила. На мой взгляд, проблема здесь больше в других инструментах или в моем непонимании.





Во-первых: обещания решателей не должны возвращаться до выполнения следующего. Это относится только к полям верхнего уровня мутаций. Во-вторых: я не думаю, что вы можете дождаться функции
nextв экспрессе. По моему опыту написания промежуточного ПО, которое нисходящее промежуточное ПО обертывания довольно сложно выразить. Но вы можете сделать это в Коа, просто также дождитесь транзакции. Если вы можете легко изменить фреймворк http, я настоятельно рекомендую фреймворк, который работает с обещаниями / ожиданием. Я понятия не имею, почему люди до сих пор используют экспресс в эпоху пост-Node 6.