Next.js调用私有环境变量API时遇500与Axios错误
葡萄酒搭配应用Next.js API路由问题排查
问题背景
我正在用Next.js开发一款葡萄酒搭配应用,用户点击葡萄酒类型(如霞多丽)时,调用第三方API获取推荐的食物搭配。使用NEXT_PUBLIC环境变量时功能正常,但不想暴露私有API密钥,于是尝试在pages/api文件夹下实现服务端API路由,结果遇到500 (Internal Server Error)和Axios错误。
了解到getStaticProps()必须在页面中调用,但我的API请求是在组件内发起的,所以无法使用该方案。
环境配置
.env.local内容:
API_KEY=<my api key> BASE_URL=https://api.spoonacular.com/
服务端API路由代码(pages/api/wineWithFood.js)
import axios from 'axios'; export default async function wineWithFood(req, res) { const { query: { wine }, } = req; const url = `${process.env.BASE_URL}food/wine/dishes?wine=${wine}&apiKey=${process.env.API_KEY}`; const response = await axios.get(url); res.status(200).json({ data: response.data, }); }
初始组件调用代码
const getPairing = async () => { axios.get(`/api/wineWithFood?wine=${wine}`, { headers: { Accept: 'application/json', 'Access-Control-Allow-Origin': '*', }, }) .then((response) => response) .then((response) => { setData(response.data.pairings) }) .catch((err) => console.log(err)) } const handleChange = (e) => { e.preventDefault(); setWine(e.target.value); getPairing(); }; console.log(wine)
初始问题
- 请求无法捕获葡萄酒类型,虽然浏览器控制台
console.log(wine)显示值正常 - 调用API时返回400状态码,提示「葡萄酒参数不能为空」
修改后的组件代码
const getPairing = async (wine) => { axios .get(`/api/wineWithFood?wine=${wine}`, { headers: { Accept: 'application/json', 'Access-Control-Allow-Origin': '*', }, }) .then((response) => response) .then((response) => { setData(response.data.pairings); console.log(response.data.pairings); }) .catch((err) => console.log(err)); }; const handleChange = (e) => { e.preventDefault(); setWine(e.target.value); getPairing(wine); };
修改后仍存在的问题
首次点击葡萄酒类型仍报错,第二次点击同一选项时,浏览器控制台显示wine值正常,终端能看到第三方API返回的正确数据,但前端依旧报500错误。
排查与修复方案
1. 解决状态更新异步导致的参数延迟问题
setWine是异步操作,修改后的handleChange中调用getPairing(wine)时,wine还是更新前的旧值。直接传递e.target.value给getPairing即可:
const handleChange = (e) => { e.preventDefault(); const selectedWine = e.target.value; setWine(selectedWine); getPairing(selectedWine); };
2. 完善API路由的错误处理与参数校验
当前API路由未做错误捕获,第三方API请求失败会直接抛出未处理错误导致500。添加参数校验、错误捕获和特殊字符处理:
import axios from 'axios'; export default async function wineWithFood(req, res) { try { const { query: { wine } } = req; // 提前校验参数,避免无效请求 if (!wine) { return res.status(400).json({ error: '葡萄酒参数不能为空' }); } // 对葡萄酒名称编码,处理空格、特殊字符 const encodedWine = encodeURIComponent(wine); const url = `${process.env.BASE_URL}food/wine/dishes?wine=${encodedWine}&apiKey=${process.env.API_KEY}`; const response = await axios.get(url); // 直接返回第三方API的数据,减少嵌套 res.status(200).json(response.data); } catch (err) { // 打印错误日志便于排查 console.error('第三方API请求失败:', err); // 返回对应错误状态码和信息 res.status(err.response?.status || 500).json({ error: err.response?.data?.error || err.message }); } }
3. 优化前端错误日志与请求头部
- 前端请求中的
Access-Control-Allow-Origin是响应头,不需要在请求中设置,直接删除该头部 - 完善错误捕获,打印完整错误信息:
const getPairing = async (wine) => { axios.get(`/api/wineWithFood?wine=${wine}`) .then((response) => { setData(response.data.pairings); console.log(response.data.pairings); }) .catch((err) => { // 打印详细错误信息,包括后端返回的错误内容 console.log('请求错误:', err.response?.data || err.message); }); };
4. 验证环境变量加载
在API路由中临时添加日志(生产环境上线前删除),确认环境变量是否正确加载:
console.log('BASE_URL:', process.env.BASE_URL); console.log('API_KEY已配置:', !!process.env.API_KEY);
内容的提问来源于stack exchange,提问作者techmeowt
相关产品推荐
相关产品推荐

