Node.js+PostgreSQL数据获取失败:TypeError及Axios报错求助
问题排查:Node.js + PostgreSQL费用管理接口数据无法获取错误
问题情况
调用接口时出现错误:Failed to retrieve expenses: TypeError: Cannot read properties of undefined (reading 'json'),同时前端提交表单时触发Axios错误,首次使用PostgreSQL,怀疑数据库连接或代码逻辑存在问题。
现有代码配置
index.js
const express = require('express') const app = express() const port = 8000 const expense_model = require('./expense_model') app.use(express.json()); app.use(function (req, res, next) { res.setHeader('Access-Control-Allow-Origin', 'http://localhost:8000'); res.setHeader('Access-Control-Allow-Methods', 'GET,POST,PUT,DELETE,OPTIONS'); res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Access-Control-Allow-Headers'); next(); }); app.get('/', (req, res) => { expense_model.getExpenses() .then(expenses => { res.status(200).json(expenses); }) .catch(error => { console.error('Failed to retrieve expenses:', error); res.status(500).json({ error: 'Failed to retrieve expenses' }); }); }); app.post('/expenses', (req, res) => { expense_model.createExpense(req.body) .then(response => { res.status(200).send(response); }) .catch(error => { res.status(500).send(error); }) }) app.delete('/expenses/:id', (req, res) => { expense_model.deleteExpense(req.params.id) .then(response => { res.status(200).send(response); }) .catch(error => { res.status(500).send(error); }) }) app.listen(port, () => console.log(`Example app listening on port ${port}!`))
expense_model.js
const Pool = require('pg').Pool const pool = new Pool({ user: 'my_user', host: 'localhost', database: 'my_database', password: 'root', port: 5432, }); async function getExpenses(req, res) { try { const query = 'SELECT * FROM expenses'; const result = await pool.query(query); const expenses = result.rows; res.status(200).json(expenses); console.log('Response:', res); } catch (err) { console.error('Failed to retrieve expenses:', err); res.status(500).json({ error: 'Failed to retrieve expenses' }); } } async function createExpense(req, res) { const { expense, amount, description, tag } = req.body; const query = 'INSERT INTO expenses (expense, amount, description, tag) VALUES ($1, $2, $3, $4) RETURNING *'; const values = [expense, amount, description, tag]; try { const result = await pool.query(query, values); res.status(201).json(result.rows[0]); } catch (err) { console.error('Failed to create an expense:', err); res.status(500).json({ error: 'Failed to create an expense' }); } } async function updateExpense(req, res) { const id = parseInt(req.params.id); const { expense, amount, description, tag } = req.body; const query = 'UPDATE expenses SET expense = $1, amount = $2, description = $3, tag = $4 WHERE id = $5'; const values = [expense, amount, description, tag, id]; try { const result = await pool.query(query, values); if (result.rowCount === 0) { res.status(404).json({ error: 'Expense not found' }); } else { res.json({ message: 'Expense updated successfully' }); } } catch (err) { console.error('Failed to update an expense:', err); res.status(500).json({ error: 'Failed to update an expense' }); } } async function deleteExpense(req, res) { const id = parseInt(req.params.id); const query = 'DELETE FROM expenses WHERE id = $1'; const values = [id]; try { const result = await pool.query(query, values); if (result.rowCount === 0) { res.status(404).json({ error: 'Expense not found' }); } else { res.json({ message: 'Expense deleted successfully' }); } } catch (err) { console.error('Failed to delete an expense:', err); res.status(500).json({ error: 'Failed to delete an expense' }); } } module.exports = { getExpenses, createExpense, deleteExpense, updateExpense }
错误原因
核心问题是model层函数设计与路由层调用不匹配:
- model里的
getExpenses、createExpense等函数定义时接收req、res参数,试图直接在model里处理响应 - 但路由层调用这些函数时,比如
expense_model.getExpenses()并没有传递req、res,导致函数内部的res是undefined,调用res.json()就会触发Cannot read properties of undefined (reading 'json')错误 - 另外CORS配置里的
Access-Control-Allow-Origin设为http://localhost:8000,如果前端运行在其他端口(比如3000),会导致跨域错误,引发Axios报错
解决方法
1. 重构model层函数,让它们只处理数据库逻辑并返回数据
修改expense_model.js,去掉函数里的req、res参数,改为返回数据或抛出错误:
const Pool = require('pg').Pool const pool = new Pool({ user: 'my_user', host: 'localhost', database: 'my_database', password: 'root', port: 5432, }); async function getExpenses() { try { const query = 'SELECT * FROM expenses'; const result = await pool.query(query); return result.rows; } catch (err) { console.error('Failed to retrieve expenses:', err); throw new Error('Failed to retrieve expenses'); } } async function createExpense(expenseData) { const { expense, amount, description, tag } = expenseData; const query = 'INSERT INTO expenses (expense, amount, description, tag) VALUES ($1, $2, $3, $4) RETURNING *'; const values = [expense, amount, description, tag]; try { const result = await pool.query(query, values); return result.rows[0]; } catch (err) { console.error('Failed to create an expense:', err); throw new Error('Failed to create an expense'); } } async function updateExpense(id, expenseData) { const { expense, amount, description, tag } = expenseData; const query = 'UPDATE expenses SET expense = $1, amount = $2, description = $3, tag = $4 WHERE id = $5'; const values = [expense, amount, description, tag, id]; try { const result = await pool.query(query, values); if (result.rowCount === 0) { throw new Error('Expense not found'); } return { message: 'Expense updated successfully' }; } catch (err) { console.error('Failed to update an expense:', err); throw err; } } async function deleteExpense(id) { const query = 'DELETE FROM expenses WHERE id = $1'; const values = [id]; try { const result = await pool.query(query, values); if (result.rowCount === 0) { throw new Error('Expense not found'); } return { message: 'Expense deleted successfully' }; } catch (err) { console.error('Failed to delete an expense:', err); throw err; } } module.exports = { getExpenses, createExpense, deleteExpense, updateExpense }
2. 修正路由层的调用逻辑
修改index.js,正确传递参数给model函数,并在路由层处理响应:
const express = require('express') const app = express() const port = 8000 const expense_model = require('./expense_model') app.use(express.json()); app.use(function (req, res, next) { // 如果前端运行在其他端口,替换成对应地址,比如http://localhost:3000 res.setHeader('Access-Control-Allow-Origin', 'http://localhost:3000'); res.setHeader('Access-Control-Allow-Methods', 'GET,POST,PUT,DELETE,OPTIONS'); res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Access-Control-Allow-Headers'); next(); }); app.get('/', async (req, res) => { try { const expenses = await expense_model.getExpenses(); res.status(200).json(expenses); } catch (error) { console.error('Failed to retrieve expenses:', error); res.status(500).json({ error: error.message }); } }); app.post('/expenses', async (req, res) => { try { const newExpense = await expense_model.createExpense(req.body); res.status(201).json(newExpense); } catch (error) { res.status(500).json({ error: error.message }); } }) app.delete('/expenses/:id', async (req, res) => { try { const result = await expense_model.deleteExpense(parseInt(req.params.id)); res.status(200).json(result); } catch (error) { if (error.message === 'Expense not found') { res.status(404).json({ error: error.message }); } else { res.status(500).json({ error: error.message }); } } }) // 补充update路由(之前代码里没用到,但model里有,可选添加) app.put('/expenses/:id', async (req, res) => { try { const result = await expense_model.updateExpense(parseInt(req.params.id), req.body); res.status(200).json(result); } catch (error) { if (error.message === 'Expense not found') { res.status(404).json({ error: error.message }); } else { res.status(500).json({ error: error.message }); } } }) app.listen(port, () => console.log(`Example app listening on port ${port}!`))
3. 验证数据库连接(可选)
如果担心数据库连接问题,可以在expense_model.js开头加一段测试代码,确认能正常连接:
// 测试数据库连接 pool.connect((err, client, release) => { if (err) { return console.error('Error acquiring client', err.stack) } client.query('SELECT NOW()', (err, result) => { release() if (err) { return console.error('Error executing query', err.stack) } console.log('数据库连接正常,当前时间:', result.rows[0]) }) })
额外提示
- 确保PostgreSQL服务正在运行,且
my_database数据库、expenses表已正确创建 - 前端Axios请求的地址要和后端端口一致,比如后端跑在8000,前端请求地址应为
http://localhost:8000/或http://localhost:8000/expenses
内容的提问来源于stack exchange,提问作者tanaya sharma
相关产品推荐
相关产品推荐

