NestJS 部署到 Vercel 完整指南

从反复踩坑到成功部署,记录 NestJS 11 + Vercel 部署的所有坑和经验。


目录


最终方案(TL;DR)

NestJS 在 Vercel 上是零配置部署。 你不需要 vercel.jsonapi/index.tsserverless-http 或任何特殊适配器。

只需确保:

  1. src/main.ts 使用标准的 NestFactory.create() + app.listen()
  2. Vercel 项目框架预设被正确识别为 NestJS(不是 Other)最好手动打开网站查看项目是否被标识为nestjs项目
  3. CORS 配置包含 OPTIONS 方法和必要的头信息

项目背景

  • 框架:NestJS 11
  • 数据库:MongoDB(通过 TypeORM)
  • 缓存:Redis(通过 ioredis)
  • 认证:JWT + Passport
  • 其他:阿里云人机验证、Resend 邮件服务、设备指纹加密

踩坑历程

坑一:用错了部署方式

最开始在网上找到的很多教程都用 @vercel/node + builds 的方式:

// ❌ 不需要这样
{
  "builds": [
    { "src": "src/main.ts", "use": "@vercel/node" }
  ],
  "routes": [
    { "src": "/(.*)", "dest": "src/main.ts" }
  ]
}

还有教程推荐用 ExpressAdapter + serverless-http 创建专门的 Serverless 入口文件 api/index.ts

// ❌ 不需要这样
import { ExpressAdapter } from '@nestjs/platform-express';
import serverless from 'serverless-http';

这些方式在你搜到的旧文章里有效,但 Vercel 现在已经原生支持 NestJS 了。 加了这些反而干扰自动检测,导致构建失败或 404。


坑二:Vercel 项目预设错误

这是最隐蔽的坑。

现象:构建日志显示 npm run build 执行成功,但随即报错:

Error: No Output Directory named "public" found after the Build completed.

原因:Vercel 项目创建时框架预设被设成了 Other(或其他前端框架),导致 Vercel 在构建后去找 public/ 目录(这是静态网站的默认输出目录),而不是 NestJS 的 dist/ 目录。

解决:在 Vercel 控制台删除项目,重新从 GitHub 导入。Vercel 会全新检测依赖包中的 @nestjs/core,自动将框架预设设为 NestJS

仅修改控制台的 Framework Preset 可能不生效(有缓存),重建项目最可靠。

验证方法:构建日志应该显示 Vercel 识别到了 NestJS:

Detected framework: NestJS
Build Command: npm run build
Output Directory: dist

坑三:CORS 预检请求失败

现象:浏览器控制台报错:

Access to XMLHttpRequest ... has been blocked by CORS policy:
Response to preflight request doesn't pass access control check:
Redirect is not allowed for a preflight request.

原因有两个:

1. CORS 配置缺少 OPTIONS 方法

NestJS 默认的 app.enableCors() 配置不够完整:

// ❌ 原始配置
app.enableCors({
  origin: true,
  methods: 'GET,HEAD,PUT,PATCH,POST,DELETE',  // 缺少 OPTIONS
  credentials: true,
});

修复:

// ✅ 完整 CORS 配置
app.enableCors({
  origin: process.env.CORS_ORIGIN || true,
  methods: 'GET,HEAD,PUT,PATCH,POST,DELETE,OPTIONS',
  allowedHeaders: 'Content-Type,Authorization,X-Requested-With,Accept',
  credentials: true,
  preflightContinue: false,
  optionsSuccessStatus: 204,
});

2. 客户端 URL 有双斜杠

请求 URL 为 https://domain.vercel.app//user/login(多了个 /),服务器会 301 重定向来规范化 URL。浏览器不允许 preflight 请求被重定向。

修复:把客户端 base URL 末尾的 / 去掉

❌ https://domain.vercel.app//user/login
✅ https://domain.vercel.app/user/login

坑四:Express 版本冲突

NestJS 11 底层使用 Express 作为 HTTP 适配器。Express 4.21+ 版本将 app.router 标记为 deprecated 并抛出 Error(不是 Warning),导致 NestFactory.create()app.init() 阶段崩溃:

Error: 'app.router' is deprecated!
    at ExpressAdapter.isMiddlewareApplied

解决:不需要在 package.json 中显式添加 express 依赖。@nestjs/platform-express 已经有正确的 Express 版本作为依赖。如果已经添加了,删除即可。


坑五:路径别名在 Vercel 上不工作

tsconfig.json 中配置的路径别名(如 "@src/*": ["src/*"])在 Vercel 的 Serverless 环境下不生效,因为 Vercel 使用 esbuild 编译且路径映射不兼容。

解决:将 src/ 开头的绝对路径改为相对路径:

// ❌ Vercel 上会报错
import { UserInfoVo } from 'src/public/vo/userInfoVo';

// ✅ 使用相对路径
import { UserInfoVo } from '../public/vo/userInfoVo';

如果需要保留别名用于本地开发,可以在 jest.config 中配置 moduleNameMapper(仅影响测试),但生产代码使用相对路径最可靠。


最终配置

src/main.ts(完整版)

import { NestFactory } from '@nestjs/core';
import { ConfigService } from '@nestjs/config';
import { AppModule } from './app.module';
import { LoggerMiddleware } from './middleware/logger/logger.middleware';
import { GlobalExceptionFilter } from './filters/global-exception.filter';
import { AutoStatusCodeInterceptor } from './filters/auto-status-code.interceptor';
import { setupGlobalValidation } from './common/pipes';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  app.enableCors({
    origin: process.env.CORS_ORIGIN || true,
    methods: 'GET,HEAD,PUT,PATCH,POST,DELETE,OPTIONS',
    allowedHeaders: 'Content-Type,Authorization,X-Requested-With,Accept',
    credentials: true,
    preflightContinue: false,
    optionsSuccessStatus: 204,
  });

  app.use(new LoggerMiddleware().use);
  app.useGlobalFilters(new GlobalExceptionFilter());
  app.useGlobalInterceptors(new AutoStatusCodeInterceptor());
  setupGlobalValidation(app);

  const configService = app.get(ConfigService);
  const port = configService.get('PORT') || 3000;
  await app.listen(port);
  console.log(`Application is running on: ${await app.getUrl()}`);
}

bootstrap();

package.json

{
  "scripts": {
    "build": "nest build",
    "start:dev": "nest start --watch",
    "start:prod": "node dist/main"
  },
  "dependencies": {
    "@nestjs/common": "^11.0.1",
    "@nestjs/core": "^11.0.1",
    "@nestjs/platform-express": "^11.0.1"
    // ... 其他业务依赖
  }
}

不需要 vercel.jsonserverless-httpexpressapi/ 目录。

Vercel 控制台设置

设置项
Framework Preset NestJS(自动检测)
Build Command npm run build(自动检测)
Output Directory dist(自动检测)
Node.js Version 22.x

部署步骤

1. 确保代码干净

# 确认没有 vercel.json(除非是空项目需要特殊配置)
# 确认没有 api/ 目录
# 所有路径引用使用相对路径
git add -A
git commit -m "chore: 准备 Vercel 部署"
git push

2. 创建 Vercel 项目

# 方式一:Vercel CLI
npm i -g vercel
vercel

# 方式二:控制台导入
# 访问 https://vercel.com/new 导入 GitHub 仓库

3. 确认框架自动检测

导入后 Vercel 会自动检测 NestJS,构建日志应显示:

Detected framework: NestJS

4. 添加环境变量

在 Vercel 控制台 → Settings → Environment Variables 中添加 .env 中的所有变量。注意密码中的特殊字符需要 URL 编码(如 @%40)。

5. 部署

vercel --prod

总结

误区 事实
需要 vercel.json 配置 builds + routes Vercel 零配置原生支持 NestJS
需要 api/index.ts + serverless-http 不需要,src/main.ts 就是入口
需要显式安装 express @nestjs/platform-express 已包含
CORS 用 origin: true + methods 就够了 需要加 OPTIONSallowedHeaderspreflightContinue: false
改控制台 Framework Preset 就能修复 有缓存,重建项目才彻底

核心经验:Vercel 对 NestJS 的原生支持已经非常成熟,不要被网上过时的教程误导。保持项目干净、信任自动检测、确保 CORS 配置完整,就能顺利部署。


本文记录于 2026-05-28,NestJS 11 + Vercel。

参考:

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐