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

Next.js AppRouter部署Vercel时SSG调用内部API遇JSON解析错误

问题:Next.js应用部署到Vercel时构建失败,内部API调用报错

我有一个Next.js应用,代码如下:

export default async function StaticSiteGenerator() {
  const res = await fetch(`https://{DOMAIN_NAME}/api/data`);
  const data = await res.json();

  return (
    <div>
      <h1>Pokémon List</h1>
      <div style={{ display: "flex", flexWrap: "wrap" }}>
        {data.map((data: any, index: any) => (
          <div key={index}>
            <p>{data.id}</p>
          </div>
        ))}
      </div>
    </div>
  );
}

将该应用部署到Vercel时,构建过程中出现如下错误:

SyntaxError: Unexpected token '<', "<!DOCTYPE "... is not valid JSON
    at JSON.parse (<anonymous>)
    at parseJSONFromBytes (node:internal/deps/undici/undici:5584:19)
    at successSteps (node:internal/deps/undici/undici:5555:27)
    at fullyReadBody (node:internal/deps/undici/undici:1665:9)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async specConsumeBody (node:internal/deps/undici/undici:5564:7)
    at async i (/vercel/path0/.next/server/app/extra/ssg/page.js:1:4401) {
  digest: '3318096297'
}

我倾向于使用内部API(而非server actions或其他替代方案),该如何解决此问题以确保构建成功?
我怀疑错误是因构建时内部API尚未就绪导致的,有趣的是本地构建完全正常。


解决方案

核心原因

你的猜测正确:Vercel静态构建阶段,应用还未部署上线,https://{DOMAIN_NAME}/api/data指向的服务尚未启动,请求返回的是404页面的HTML(即错误中的<!DOCTYPE...),而非预期的JSON,因此解析失败。本地构建正常是因为本地开发服务器同时运行了前端和API路由,请求能正常命中。

解决方法(使用内部API的正确姿势)

方案1:直接调用API路由的服务器端逻辑(推荐)

在Next.js中,无需通过HTTP请求访问外部域名,直接调用API路由的核心逻辑即可:

  1. 抽离API逻辑到独立文件
    假设你的/api/data路由代码在app/api/data/route.ts,把数据获取逻辑抽离到公共文件:

    // app/lib/data.ts
    export async function getPokemonData() {
      // 这里放置原本/api/data路由中的业务逻辑,比如从数据库或外部数据源拉取数据
      const response = await fetch("https://pokeapi.co/api/v2/pokemon?limit=10");
      const data = await response.json();
      return data.results;
    }
    
  2. 在页面组件中直接调用该函数
    修改页面组件,替换HTTP请求为直接调用逻辑函数:

    import { getPokemonData } from "@/app/lib/data";
    
    export default async function StaticSiteGenerator() {
      const data = await getPokemonData();
    
      return (
        <div>
          <h1>Pokémon List</h1>
          <div style={{ display: "flex", flexWrap: "wrap" }}>
            {data.map((item: any, index: number) => (
              <div key={index}>
                <p>{item.name}</p>
              </div>
            ))}
          </div>
        </div>
      );
    }
    

方案2:保留HTTP调用(不推荐,仅满足需求)

如果坚持要通过HTTP请求内部API,可以在构建阶段使用相对路径或环境判断,但需注意构建阶段的局限性:

export default async function StaticSiteGenerator() {
  // 构建阶段使用本地地址,生产环境用正式域名
  const baseUrl = process.env.NODE_ENV === "production" 
    ? "https://{DOMAIN_NAME}" 
    : "http://localhost:3000";
  const res = await fetch(`${baseUrl}/api/data`);
  
  // 先校验响应状态,避免解析失败
  if (!res.ok) {
    throw new Error(`API请求失败: ${res.status}`);
  }
  
  const data = await res.json();

  return (
    <div>
      <h1>Pokémon List</h1>
      <div style={{ display: "flex", flexWrap: "wrap" }}>
        {data.map((data: any, index: any) => (
          <div key={index}>
            <p>{data.id}</p>
          </div>
        ))}
      </div>
    </div>
  );
}

注意:这种方法在Vercel静态构建时仍可能失败,因为构建阶段API服务未启动,因此方案1是更可靠的选择。

额外提示

  • 静态生成页面的数据获取,优先使用服务器端直接逻辑调用,既高效又能避免网络请求的不确定性。
  • 若必须调用外部HTTP API,确保该API在构建阶段是独立部署且始终可用的。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 20:34:58