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

Next.js App Router服务端Fetch打包报错:localhost连接被拒绝

问题分析
  • 报错核心原因:npm run build 执行时,Next.js 会进入静态页面预渲染阶段,此时本地开发服务器(localhost:3000)并未启动,导致服务端代码中 fetch("http://localhost:3000/api/posts") 无法建立连接,抛出 ECONNREFUSED 错误。
  • 为什么 npm run dev 无异常?因为开发模式下本地服务器处于运行状态,服务端的 fetch 请求能正常访问到本地 API 路由。
解决方案

方案1:直接调用API逻辑函数(推荐)

不需要通过 HTTP 请求访问本地 API,直接复用 API 路由里的数据处理逻辑,既消除网络请求开销,也彻底避免构建时的连接问题。

步骤:

  1. 提取 API 路由中的数据获取逻辑到单独的工具文件,比如 lib/posts.js:
// lib/posts.js
// 这里写原来 app/api/posts/route.js 中的数据库查询或数据处理代码
export async function getPosts() {
  // 示例:假设你用 Prisma 查询数据库
  // const posts = await prisma.post.findMany();
  // return posts;
  
  // 替换成你实际的数据获取逻辑
  const mockPosts = [/* 你的数据结构 */];
  return mockPosts;
}
  1. 在 Blog 页面中直接调用该函数:
import React from "react";
import styles from "./page.module.css";
import Link from "next/link";
import Image from "next/image";
import { getPosts } from "@/lib/posts";

async function getData() {
  const posts = await getPosts();
  if (!posts) {
    throw new Error("Failed to fetch data");
  }
  return posts;
}

const Blog = async () => {
  const data = await getData();
  return (
    <div className={styles.mainContainer}>
      {data.map((item) => (
        <Link
          href={`/blog/${item._id}`}
          className={styles.container}
          key={item._id}
        >
          <div className={styles.imageContainer}>
            <Image
              src={item.img}
              alt=""
              width={400}
              height={250}
              className={styles.image}
            />
          </div>
          <div className={styles.content}>
            <h1 className={styles.title}>{item.title}</h1>
            <p className={styles.desc}>{item.desc}</p>
          </div>
        </Link>
      ))}
    </div>
  );
};

export default Blog;

方案2:强制页面动态渲染

如果必须保留 fetch 请求,可以通过配置让页面跳过预渲染,改为每次请求时动态生成,这样构建阶段不会执行服务端的 fetch 逻辑:

在 Blog 页面顶部添加动态渲染配置:

import React from "react";
import styles from "./page.module.css";
import Link from "next/link";
import Image from "next/image";

// 强制页面为动态渲染,构建时不预渲染
export const dynamic = "force-dynamic";

async function getData() {
  const res = await fetch("http://localhost:3000/api/posts", {
    cache: "no-store",
  });

  if (!res.ok) {
    throw new Error("Failed to fetch data");
  }

  return res.json();
}

const Blog = async () => {
  const data = await getData();
  return (
    <div className={styles.mainContainer}>
      {data.map((item) => (
        <Link
          href={`/blog/${item._id}`}
          className={styles.container}
          key={item._id}
        >
          <div className={styles.imageContainer}>
            <Image
              src={item.img}
              alt=""
              width={400}
              height={250}
              className={styles.image}
            />
          </div>
          <div className={styles.content}>
            <h1 className={styles.title}>{item.title}</h1>
            <p className={styles.desc}>{item.desc}</p>
          </div>
        </Link>
      ))}
    </div>
  );
};

export default Blog;

方案3:环境变量区分环境

通过环境变量配置不同环境的 API 地址,开发时用 localhost,生产时用远程服务器地址:

  1. 在项目根目录创建 .env.local(开发环境):
NEXT_PUBLIC_API_BASE_URL=http://localhost:3000/api
  1. 生产环境配置 .env 文件:
NEXT_PUBLIC_API_BASE_URL=https://your-production-domain.com/api
  1. 代码中使用环境变量:
async function getData() {
  const res = await fetch(`${process.env.NEXT_PUBLIC_API_BASE_URL}/posts`, {
    cache: "no-store",
  });

  if (!res.ok) {
    throw new Error("Failed to fetch data");
  }

  return res.json();
}

内容的提问来源于stack exchange,提问作者Ders Çalışma zamanı

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 17:05:21