Node.js查询PostgreSQL时读取undefined的gameid属性报错
问题根因
- 直接触发报错的原因非常明确:查询
public.highscores表时,没有匹配到传入id的记录,返回结果rowCount为0、rows为空数组,res.rows[0]值为undefined,你把这个undefined赋值给highscore变量后,后续访问highscore.gameid自然抛出TypeError: Cannot read properties of undefined (reading 'gameid')。 - 之前代码能正常运行纯粹是巧合:当时数据库里刚好存在对应id的高分记录,后续你手动删除过数据、调用函数时传入了不存在的id、甚至连错了空的数据库实例,都会触发这个问题,和EJS、PostgreSQL服务本身故障无关。
- 现有代码存在多处致命隐患,哪怕这次补了数据临时恢复,后续也会反复出问题:
- 没有任何查询结果空值校验逻辑,只要查不到数据就会直接崩溃
- 直接拼接SQL字符串传参,存在SQL注入漏洞,恶意参数可以直接拖库甚至删表
- 每次调用函数都新建一个数据库连接池,会快速占满数据库连接,最终导致服务无法连接数据库
- 混用
async/await和.then链式调用,没有全局错误捕获,任何数据库异常都会直接打崩整个服务
修复方案
替换为以下实现即可解决当前崩溃问题,同时修复所有潜在隐患:
const { Pool } = require('pg'); // 数据库连接池全局只初始化一次,禁止写在函数内部重复创建 const pool = new Pool(credentials); const getHighscore = async (id) => { // 入参合法性校验 if (typeof id !== 'number') { throw new Error('查询高分记录需传入合法数字类型id'); } try { // 使用参数化查询,禁止直接拼接SQL字符串 const highscoreResult = await pool.query( 'SELECT * FROM public.highscores WHERE id = $1', [id] ); // 查不到数据直接返回null,交由上层业务处理不存在的场景 if (!highscoreResult.rowCount) { return null; } const highscore = highscoreResult.rows[0]; // 关联字段不存在则跳过关联表查询 if (!highscore.gameid) { highscore.game = null; return highscore; } const gameResult = await pool.query( 'SELECT * FROM public.games WHERE id = $1', [highscore.gameid] ); highscore.game = gameResult.rows[0] || null; return highscore; } catch (err) { console.error('查询高分记录出错,查询id为:', id, err); throw err; } };
- 核心改动点:
- 连接池全局复用,避免连接泄漏、资源浪费
- 全量替换为参数化查询,彻底杜绝SQL注入风险
- 每一步操作都做合法性校验,不会出现访问
undefined属性的问题 - 统一用
try/catch捕获异常,错误日志带上查询参数方便后续排查 - 去掉冗余的
.then写法,逻辑线性可读,避免逻辑混乱
后续排查步骤
如果改完代码后查询仍返回null,按顺序排查即可:
- 打印调用
getHighscore时传入的id值,确认没有传undefined、null或者错误的id - 用数据库客户端直连代码配置的数据库实例,执行
SELECT * FROM public.highscores WHERE id = 你传入的id,确认记录存在——你之前手动删除过库数据,大概率是把对应高分记录删掉了 - 检查数据库连接配置,确认没有连错空的测试库、其他环境的数据库实例
内容的提问来源于stack exchange,提问作者Vanfyr
相关产品推荐
相关产品推荐

