部署至Vercel后Next.js中Axios调用API出现500错误求助
Next.js + Nodemailer 部署Vercel后500错误排查
问题描述
我用Next.js实现了一个基于axios和nodemailer的邮件发送API,本地运行完全正常,但部署到Vercel后触发邮件发送请求时,前端捕获到500错误。
前端表单提交代码
async function onSubmitForm(values) { setLoading(true); let config = { method: "post", url: `${env.process.NEXT_LINK}/api/contact`, headers: { "Content-Type": "application/json", }, data: values, }; try { const response = await axios(config); setLoading(false); setSuccess(true); } catch (err) { console.log("frontend error", err); } }
前端捕获的错误信息
frontend error AxiosError { message: 'Request failed with status code 500', name: 'AxiosError', code: 'ERR_BAD_RESPONSE', config: {…}, request: XMLHttpRequest, response: {data: '', status: 500, statusText: '', headers: {…}, config: {…}, …} }
Contact API 代码
import handlebars from "handlebars"; const nodemailer = require("nodemailer"); const path = require("path"); const fs = require("fs"); export default async (req, res) => { const { firstName, name, email, message, phone } = req.body; const transporter = nodemailer.createTransport({ host: "smtp.gmail.com", port: 465, secure: true, auth: { user: process.env.NEXT_USER, pass: process.env.NEXT_GMAIL, }, }); await new Promise((resolve, reject) => { // verify connection configuration transporter.verify(function (error, success) { if (error) { console.log(error); reject(error); } else { console.log("Server is ready to take our messages"); resolve(success); } }); }); const filePath = path.join(__dirname, "../../../../components/form/html/template.html"); const source = fs.readFileSync(filePath, "utf-8").toString(); const template = handlebars.compile(source); const replacements = { name: name, email: email, phone: phone, message: message, }; const htmlToSend = template(replacements); await new Promise((resolve, reject) => { if (!firstName) { try { const emailRes = transporter.sendMail({ from: email, to: {process.env.NEXT_MYEMAIL}, subject: `Email von ${name}`, html: htmlToSend, }); console.log("Message Sent", emailRes.messageId); res.status(200).json(req.body); } catch (err) { console.log("GEHT NET", err); } } else { res.status(403).json(req.body); } }); console.log(req.body, "Test"); };
package.json 内容
{ "name": "default_next", "version": "0.1.0", "private": true, "main": "index.js", "scripts": { "dev": "next dev", "build": "next build", "start": "next start", "export": "next build && next export", "lint": "next lint" }, "dependencies": { "@amir04lm26/react-modern-calendar-date-picker": "^1.0.1", "@palmabit/react-cookie-law": "^0.7.0", "@portabletext/react": "^1.0.6", "@sanity/asset-utils": "^1.3.0", "@sanity/client": "^3.3.2", "@sanity/image-url": "^1.0.1", "animate.css": "^4.1.1", "axios": "^0.27.2", "framer-motion": "^7.1.0", "handlebars": "^4.7.7", "next": "12.1.6", "next-sanity-image": "^3.2.1", "next-useragent": "^2.8.0", "nodemailer": "^6.7.8", "react": "18.2.0", "react-animate-on-scroll": "^2.1.5", "react-collapsed": "^3.3.2", "react-cookie-consent": "^8.0.1", "react-device-detect": "^2.2.2", "react-dom": "18.2.0", "react-hook-form": "^7.34.2", "react-icons": "^4.4.0", "react-loader-spinner": "^5.3.3", "react-modern-calendar-datepicker": "^3.1.6", "react-spring": "^9.4.5", "react-sticky": "^6.0.3", "react-use-scroll-snap": "0.0.4", "react-visibility-sensor": "^5.1.1", "swiper": "^8.3.0" }, "devDependencies": { "autoprefixer": "^10.4.7", "eslint": "8.17.0", "eslint-config-next": "12.1.6", "eslint-plugin-react": "^7.30.1", "postcss": "^8.4.14", "tailwindcss": "^3.1.3" } }
已尝试的操作
- 在Vercel中配置了所有环境变量
- 验证过API路径正确(曾因路径问题出现404,已修复)
- 尝试过硬编码API路径
- 本地运行始终正常,但部署后问题依旧
解决方案
1. 修复Gmail SMTP授权问题
Vercel服务器环境无法像本地一样通过已登录状态获得Gmail授权,需调整账号设置:
- 若开启了2FA:在Google账号设置中生成应用专用密码,替换
NEXT_GMAIL环境变量的值 - 若未开启2FA:临时开启不太安全的应用访问(推荐优先开启2FA后用专用密码,更安全)
2. 修正模板文件路径
本地__dirname的路径结构和Vercel生产环境不一致,导致找不到模板文件,改用项目根目录路径:
// 替换原filePath代码 const filePath = path.join(process.cwd(), "components/form/html/template.html");
process.cwd()指向项目根目录,不受构建环境影响。
3. 修复sendMail的Promise处理与语法错误
原代码存在两处问题:
to: {process.env.NEXT_MYEMAIL}多了冗余大括号,导致参数格式错误transporter.sendMail未正确处理异步回调,错误无法被捕获
修正后代码:
await new Promise((resolve, reject) => { if (!firstName) { transporter.sendMail({ from: email, to: process.env.NEXT_MYEMAIL, subject: `Email von ${name}`, html: htmlToSend, }, (err, info) => { if (err) { console.log("GEHT NET", err); reject(err); res.status(500).json({ error: err.message }); } else { console.log("Message Sent", info.messageId); resolve(info); res.status(200).json(req.body); } }); } else { res.status(403).json(req.body); resolve(); } });
4. 验证环境变量加载状态
在API代码开头添加日志,确认Vercel是否正确加载环境变量:
console.log("Loaded ENV - USER:", process.env.NEXT_USER); console.log("Loaded ENV - MYEMAIL:", process.env.NEXT_MYEMAIL);
然后去Vercel控制台的函数日志中查看输出,排查变量是否拼写错误或未更新。
5. 查看Vercel函数日志
直接去Vercel控制台找到api/contact对应的函数,查看执行日志,里面会显示500错误的具体原因(如认证失败、文件不存在等),是最直接的排查方式。
内容的提问来源于stack exchange,提问作者Johannes Buchner
相关产品推荐
相关产品推荐

