ES6 Async/Await, ExpressJS and Postgres transactions



修订问题

我修改了这个问题,希望得到一个更清楚的答案。


我正在尝试根据传入的req.body和表中的现有数据在ExpressJS中处理数据。

我收到一个req.body,其中包含一个更新字段的JSON列表。其中一些字段存储为Postgres中的JSONB。如果传入字段是JSONB,那么发出请求的表单(外部代码(已经运行了jsonpatch.compare()来生成修补程序列表,并且传入的是这些修补程序,而不是完整的值。对于任何非JSONB值,传入值只需要传递到UPDATE查询。

我有一个工作版本,如下所示,它假设表中现有的JSONB值为NULL。显然,这不是所需要的。我需要从数据库中提取值。不查询当前值的版本和最低路由器,如下所示:

const express = require('express')
const bodyParser = require('body-parser')
const SQL = require('sql-template-strings')
const { Client } = require('pg')
const dbConfig = require('../db')
const jsonpatch = require('fast-json-patch')
const FormRouter = express.Router()
I have some update code:
````javascript
const patchFormsRoute = (req, res) => {
const client = new Client(dbConfig)
const { id } = req.body
const parts = []
const params = [id]
// list of JSONB fields for the 'forms' table
const jsonFields = [
'sections',
'editors',
'descriptions',
]
// list of all fields, including JSONB fields in the 'forms' table
const possibleFields = [
'status',
'version',
'detail',
'materials',
...jsonFields,
]
// this is a DUMMY RECORD instead of the result of a client.query 
let currentRecord = { 'sections':[], 'editors':[], 'descriptions':[] }
possibleFields.forEach(myProp => {
if (req.body[myProp] != undefined) {
parts.push(`${myProp} = $${params.length + 1}`)
if (jsonFields.indexOf(myProp) > -1) {
val = currentRecord[myProp]
jsonpatch.applyPatch(val, req.body[myProp])
params.push(JSON.stringify(val))
} else {
params.push(req.body[myProp])
}
}
})
const updateQuery = 'UPDATE forms SET ' + parts.join(', ') + ' WHERE id = $1'
client.connect()
return client
.query(updateQuery, params)
.then(result => res.status(200).json(result.rowCount))
.catch(err => res.status(400).json(err.severity))
.then(() => client.end())
}
FormRouter.route('/')
.patch(bodyParser.json({ limit: '50mb' }), patchFormsRoute)
exports.FormRouter = FormRouter

我保证,这是工作代码,它几乎满足了我的需求。但是,我想用表中已经存在的同时获取的数据来替换伪记录。我的问题是,因为多个客户端可能同时更新一行(但查看JSONB值的正交元素(,所以我需要将fetch、calc和update作为SINGLE TRANSACTIOn进行。我的计划是:

  1. 开始事务

  2. 根据传入的id,向Postgres查询当前行值

  3. 对于任何JSONB字段,应用补丁以在UPDATE语句中为该字段生成正确的值。

  4. 使用适当的参数值(来自req.body或修补行,取决于字段是否为JSONB(运行UPDATE语句

  5. COMMIT事务,或出现错误时ROLLBACK。

我已经尝试实现@midrizi的答案;也许只有我一个人,但res的等待和简单测试的结合将服务器发送到Hyperspace。。。并且以超时结束。

如果有人仍然清醒,这里有一个有效的解决方案。

TLDR;RTFM:一个带有async/await的池化客户端(目前(。

const patchFormsRoute = (req, res) => {
const { id } = req.body
// list of JSONB fields for the 'forms' table
const jsonFields = [
'sections',
'editors',
'descriptions',
]
// list of all fields, including JSONB fields in the 'forms' table
const possibleFields = [
'status',
'version',
'detail',
'materials',
...jsonFields,
]
const parts = []
const params = [id]
;(async () => {
const client = await new Client(dbConfig)
await client.connect()
try {
// begin a transaction
await client.query('BEGIN')
// get the current form data from DB
const fetchResult = await client.query(
SQL`SELECT * FROM forms WHERE id = ${id}`,
)
if (fetchResult.rowCount === 0) {
res.status(400).json(0)
await client.query('ROLLBACK')
} else {
const currentRecord = fetchResult.rows[0]
// patch JSONB values or update non-JSONB values
let val = []
possibleFields.forEach(myProp => {
if (req.body[myProp] != undefined) {
parts.push(`${myProp} = $${params.length + 1}`)
if (jsonFields.indexOf(myProp) > -1) {
val = currentRecord[myProp]
jsonpatch.applyPatch(val, req.body[myProp])
params.push(JSON.stringify(val))
} else {
params.push(req.body[myProp])
}
}
})
const updateQuery =
'UPDATE forms SET ' + parts.join(', ') + ' WHERE id = $1'
// update record in DB
const result = await client.query(updateQuery, params)
// commit transaction
await client.query('COMMIT')
res.status(200).json(result.rowCount)
}
} catch (err) {
await client.query('ROLLBACK')
res.status(400).json(err.severity)
throw err
} finally {
client.end()
}
})().catch(err => console.error(err.stack))
}

最新更新