You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

部署至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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 19:25:20