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

Next.js结合自定义Express服务器:如何区分服务端与客户端环境?

优雅处理Next.js服务端/客户端API地址差异的方案

我完全懂你这种感受——直接写typeof window === 'undefined'虽然能解决问题,但总感觉有点“硬编码判断”的hack感,不够优雅。结合你用Next.js+自定义Express容器的场景,给你几个更干净的实现方式:

方案一:通过环境变量+封装工具函数隔离判断

把地址判断逻辑封装到统一的工具函数里,业务代码完全不用关心环境差异:

  1. 先在项目根目录的环境变量文件里配置:

    • 服务端专用API地址(不会暴露给客户端):API_URL=http://localhost:3000
    • 客户端专用API地址(带NEXT_PUBLIC_前缀,客户端可见):NEXT_PUBLIC_API_URL=https://mysite.com
  2. 封装一个通用的fetch工具函数:

// utils/fetchData.js
export const fetchData = async (endpoint) => {
  // 只在工具函数里做一次环境判断,业务代码不用重复写
  const baseUrl = typeof window === 'undefined' 
    ? process.env.API_URL 
    : process.env.NEXT_PUBLIC_API_URL;
  
  const response = await fetch(`${baseUrl}${endpoint}`);
  if (!response.ok) throw new Error('请求失败');
  return response.json();
};

之后在业务代码里直接调用fetchData('/api/posts')就行,不用再管是服务端还是客户端。

方案二:用Express反向代理统一API路径(最推荐)

这个方案彻底消除环境判断,不管服务端还是客户端都用相同的路径,由Express帮你转发请求:

  1. 先安装代理依赖:
npm install http-proxy-middleware
  1. 在你的Express服务器代码里添加代理配置:
// server.js
const express = require('express');
const { createProxyMiddleware } = require('http-proxy-middleware');
const next = require('next');

const dev = process.env.NODE_ENV !== 'production';
const nextApp = next({ dev });
const handle = nextApp.getRequestHandler();

const app = express();

// 把所有/api开头的请求代理到内部localhost地址
app.use('/api', createProxyMiddleware({
  target: 'http://localhost:3000', // 你的内部服务地址
  changeOrigin: true,
}));

nextApp.prepare().then(() => {
  app.all('*', (req, res) => handle(req, res));
  
  app.listen(3000, () => {
    console.log('服务器运行在 http://localhost:3000');
  });
});

这样不管是服务端渲染时的fetch,还是客户端的fetch,都直接用/api/xxx的相对路径:

  • 服务端请求/api/xxx时,Express直接转发到内部的http://localhost:3000/api/xxx
  • 客户端请求/api/xxx时,会自动拼接成https://mysite.com/api/xxx,然后通过Express反向代理到内部服务

这种方式最干净,业务代码完全不用关心环境差异,也不需要任何判断逻辑。

方案三:利用Next.js路由上下文判断(Pages Router专属)

如果你用的是Pages Router,可以在getServerSideProps或getInitialProps里通过上下文对象判断服务端环境,直接在服务端用内部地址获取数据,客户端则自动用外部地址:

// pages/posts.js
export async function getServerSideProps(context) {
  // 这里肯定是服务端环境,直接用localhost地址
  const res = await fetch('http://localhost:3000/api/posts');
  const posts = await res.json();
  
  return { props: { posts } };
}

// 客户端组件里直接用外部地址,或者用相对路径
export default function Posts({ posts }) {
  const fetchMorePosts = async () => {
    const res = await fetch('/api/posts?page=2');
    // ...
  };
  
  return <div>{/* 渲染内容 */}</div>;
}

总结

  • 最推荐方案二(反向代理):彻底隔离环境差异,代码最简洁
  • 如果不想用代理,方案一能把判断逻辑封装起来,避免业务代码冗余
  • Pages Router项目可以结合方案三,在服务端数据获取阶段直接用内部地址

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 11:46:08