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

Next.js 13部署Vercel构建失败,求非Server Action的外部请求方案

问题描述

我在Next.js 13的API路由中写了以下代码:

api/home/route.ts

import { NextResponse } from "next/server"
import API from "@/lib/axios"

export async function GET(req: Request, res: Response) {
  try {
    const response = await API.get("/landing")
    return NextResponse.json(response.data)
  } catch (error: any) {
    console.log(error)

    return NextResponse.json({ message: error.message }, { status: 500 })
  }
}

调用该API的代码如下:

lib/utils.ts

export async function fetchData(path: string) {
  let url

  if (process.env.ENVIRONMENT === "local") {
    url = "http://localhost:3000" + path
  } else if (process.env.NODE_ENV === "production") {
    // Production environment
    url = path
  }
  const response = await fetch(`${url}`)
  return await response.json()
}

落地页中的使用方式:

import { fetchData } from "@/lib/utils"

export default async function Home() {
  const data = await fetchData("/api/home")
}

本地yarn dev运行正常,本地yarn build也能成功,但部署到Vercel时构建失败。另外联系页也用了类似方案:创建/api/contact路由接收POST请求,调用Laravel后端发邮件,该路由在用户提交表单时触发。

需要解决两个问题:

  1. 排查Vercel部署失败的原因
  2. 在不使用实验性Server Action的前提下,向外部服务器发起POST/GET请求的最佳方案

问题排查与解决方案

一、Vercel构建失败原因

部署失败的核心原因是构建阶段预渲染时请求了未启动的内部API路由:
Next.js 13 App Router的页面组件默认是服务端组件,构建阶段会预渲染页面。你的Home是async组件,构建时会执行fetchData("/api/home"),生产环境下直接请求/api/home,但Vercel构建环境是孤立的,此时API路由还未启动,导致请求失败。本地构建正常是因为本地环境可能还运行着开发服务器,和Vercel的构建逻辑存在差异。

二、优化方案(不使用Server Action)

方案1:跳过Next.js中转路由,直接调用外部API

既然/api/home只是中转层,直接在页面组件里调用后端接口即可,省去中间跳转:

// Home页面组件
import API from "@/lib/axios"

export default async function Home() {
  try {
    const response = await API.get("/landing")
    const data = response.data
    // 渲染数据逻辑
  } catch (error: any) {
    // 错误处理
  }
}

优势:减少请求链路,提升性能,彻底避免构建阶段的内部请求问题。

方案2:抽离业务逻辑,避免构建阶段HTTP请求

如果必须保留Next.js中转API(比如统一处理鉴权、请求头),可以把API路由的业务逻辑抽离成独立函数,页面和API路由直接调用该函数:

  1. 抽离逻辑到lib/api/landing.ts:
import API from "@/lib/axios"

export async function getLandingData() {
  try {
    const response = await API.get("/landing")
    return response.data
  } catch (error: any) {
    throw new Error(error.message)
  }
}
  1. 修改api/home/route.ts:
import { NextResponse } from "next/server"
import { getLandingData } from "@/lib/api/landing"

export async function GET() {
  try {
    const data = await getLandingData()
    return NextResponse.json(data)
  } catch (error: any) {
    return NextResponse.json({ message: error.message }, { status: 500 })
  }
}
  1. 修改页面组件:
import { getLandingData } from "@/lib/api/landing"

export default async function Home() {
  const data = await getLandingData()
  // 渲染数据逻辑
}

这样页面直接调用逻辑函数,无需通过HTTP请求,规避了构建阶段的内部API访问问题。

方案3:POST请求场景(联系页表单)

对于用户交互触发的POST请求(比如联系表单提交),不会在构建阶段执行,现有API路由方案是可行的,只需确保请求处理逻辑正确:

  1. 完善api/contact/route.ts:
import { NextResponse } from "next/server"
import API from "@/lib/axios"

export async function POST(req: Request) {
  try {
    const body = await req.json()
    const response = await API.post("/send-email", body)
    return NextResponse.json(response.data)
  } catch (error: any) {
    return NextResponse.json({ message: error.message }, { status: 500 })
  }
}
  1. 客户端调用(联系页组件):
async function handleSubmit(formData: FormData) {
  const res = await fetch("/api/contact", {
    method: "POST",
    body: JSON.stringify(Object.fromEntries(formData)),
    headers: { "Content-Type": "application/json" }
  })
  const data = await res.json()
  // 处理响应逻辑
}

总结

  • Vercel构建失败的核心是预渲染阶段请求未启动的内部API,解决思路是避免构建阶段发起内部HTTP请求,要么直接调用业务逻辑,要么跳过中转层。
  • 客户端触发的POST请求(如表单提交),现有API路由方案可正常使用,只需确保请求处理逻辑正确。

内容的提问来源于stack exchange,提问作者psudo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 02:58:19