PostgreSQL+Node.js+Express连接代码修正及POST请求问题排查
解决Postman POST请求的两个核心问题(Node.js/Express/PostgreSQL)
让我们一步步拆解并解决你遇到的这两个问题:
问题1:username存入数据库为null的原因及修复
首先看你的代码,根本原因是变量名错误,和关联用户表无关,具体问题出在这几处:
- 你构造
data对象时,把req.body.username赋值给了author字段,但后续插入时却用了data.username——这个属性根本不存在,所以会传入null! - 第一个INSERT语句返回的是
id(你写的RETURNING id),但你后面却用了res.rows[0].pid,这里应该是res.rows[0].id,否则也会导致pid字段错误甚至插入失败。 created_on字段你传了字符串'now()',PostgreSQL会把它当成普通字符串存储,而不是当前时间,应该直接用NOW()(不带引号)或者new Date()。
修复后的核心代码片段(原回调版本):
// 第一个查询修复获取id db.query(query, ['value1', new Date()], (err, table1Res) => { if (shouldAbort(err)) return; const data = { author: req.body.username, // 这里是author title: req.body.title, // ...其他字段 }; const insertPostValues = [ table1Res.rows[0].id, // 用id代替pid data.author, // 用data.author代替data.username data.title, // ...其他字段 NOW() // 不带引号,或者用new Date() ]; // ...后续代码 });
问题2:请求一直处于发送状态的原因及async/await重构
你的请求一直挂起,是因为整个函数没有调用res.send()/res.json()来结束响应,而且回调嵌套太深,很容易遗漏错误处理和响应逻辑。另外,原有的事务处理如果出错,也没有回滚和返回错误信息。
下面是改用async/await的完整重构方案,代码更清晰,逻辑更可靠:
第一步:重构数据库连接文件 db/index.js
将原有的回调风格改成Promise/async支持的版本:
const { Pool } = require('pg') const pool = new Pool({ user: '', // 填入你的数据库用户名 host: 'localhost', database: '', // 填入你的数据库名 password: '', // 填入你的数据库密码 port: 5432 // PostgreSQL默认端口,根据你的配置修改 }) // 封装async版query方法 const query = async (text, params) => { const start = Date.now() const res = await pool.query(text, params) const duration = Date.now() - start console.log('executed query', { text, duration, rows: res.rowCount }) return res } // 封装async版getClient方法(用于事务) const getClient = async () => { const client = await pool.connect() const originalQuery = client.query const timeout = setTimeout(() => { console.error('客户端已被占用超过5秒!') console.error('最后执行的查询:', client.lastQuery) }, 5000) // 猴子补丁追踪最后执行的查询 client.query = (...args) => { client.lastQuery = args return originalQuery.apply(client, args) } // 封装释放客户端的逻辑 const release = () => { clearTimeout(timeout) client.query = originalQuery client.release() } return { client, release } } module.exports = { query, getClient }
第二步:重构路由处理函数 router/routefile.js
改成async函数,完善事务处理和响应逻辑:
exports.addKeyword = async (req, res) => { let dbClient; try { // 获取客户端用于事务操作 const { client, release } = await db.getClient(); dbClient = client; // 开启事务 await dbClient.query('BEGIN'); // 第一步插入Table1,获取返回的id const table1Query = 'INSERT INTO Table1 (post_type, created_on) VALUES($1, $2) RETURNING id'; const table1Result = await dbClient.query(table1Query, ['value1', new Date()]); const table1Id = table1Result.rows[0].id; // 构造要插入的数据 const data = { author: req.body.username, title: req.body.title, description: req.body.description, body: req.body.body, category: req.body.category, search_volume: req.body.search_volume, is_deleted: req.body.is_deleted || false // 给默认值避免null }; // 插入m_keyword的SQL语句 const insertPostText = ` INSERT INTO m_keyword ( pid, author, title, description, body, category, search_volume, is_deleted, created_on ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, NOW()) `; const insertPostValues = [ table1Id, data.author, data.title, data.description, data.body, data.category, data.search_volume, data.is_deleted ]; // 执行插入 await dbClient.query(insertPostText, insertPostValues); // 提交事务 await dbClient.query('COMMIT'); // 返回成功响应,结束请求 res.status(201).json({ success: true, message: '记录创建成功!', pid: table1Id }); } catch (err) { // 出错时回滚事务 if (dbClient) { await dbClient.query('ROLLBACK'); } console.error('事务处理失败:', err.stack); // 返回错误响应,结束请求 res.status(500).json({ success: false, message: '创建记录失败', error: err.message }); } finally { // 无论成功失败,都释放客户端回连接池 if (dbClient) { dbClient.release(); } } };
关键改进点:
- 用
async/await替代回调嵌套,代码可读性和可维护性大幅提升; - 确保在成功和失败场景下都调用
res.json()返回响应,不会让请求一直挂起; - 完善的事务处理:出错时自动回滚,避免脏数据;
- 添加了默认值(比如
is_deleted: req.body.is_deleted || false),减少意外的null值; - 用
new Date()或NOW()正确处理时间字段。
内容的提问来源于stack exchange,提问作者Jetro Olowole
相关产品推荐
相关产品推荐

