NestJS 发布v12版本,看一下有哪些改动

NestJS v12 正式发布:ESM 一等公民、Zod 原生验证、CLI 全面重写,这次改动真的很大

本文基于 NestJS v12 官方 Release Notes 翻译并整理,加入了个人解读和迁移建议。如果你正在考虑升级,这篇文章应该能帮你快速判断值和该注意什么。


一句话总结

NestJS v12 的核心主题是现代化:核心库全面 ESM 化、Standard Schema 验证一等公民支持、原生可观测性、CLI 从零重写。这不是一次"加点小功能"的更新,而是 NestJS 在工具链和生态定位上的一次大跃进。

先上运行环境硬指标:Node.js v20.19+ 或 v22.12+,21.x 系列正式出局。


新特性解读

ESM:终于可以选了,而且不强制

核心库现在以 ESM 格式发布,nest new 会让你选择 ESM 还是 CommonJS。但最关键的一点是------不强制迁移。已有的 CommonJS 项目借助 Node.js 的互操作能力可以继续跑。

这对我们意味着什么? 新项目可以直接上 ESM,不用再搞各种 workaround;老项目不用急,按自己的节奏来。说实话,这个策略很务实,没搞一刀切,降低了升级的心理负担。

Standard Schema 一等公民:告别 class-validator 的时代来了

这可能是 v12 最让人兴奋的特性。路由装饰器(@Body()@Param()@Query())现在直接接受一个 schema 参数,支持任何符合 Standard Schema 规范的库------Zod、Valibot、ArkType,随你挑。

typescript 复制代码
import { Body, Controller, Post } from '@nestjs/common';
import { z } from 'zod';

const CreateUserSchema = z.object({
  name: z.string().min(1),
  email: z.string().email(),
  age: z.number().int().positive(),
});

@Controller('users')
export class UsersController {
  @Post()
  create(@Body({ schema: CreateUserSchema }) dto: z.infer<typeof CreateUserSchema>) {
    return dto;
  }
}

不需要 class-validator,不需要 class-transformer,不需要 ValidationPipe 配合 DTO class。一个 Zod schema 搞定验证和类型推断。

要启用这个功能,需要注册 StandardSchemaValidationPipe;响应序列化则用 StandardSchemaSerializerInterceptor。老的 class-based 方式完全保留,两套可以共存。

这对我们意味着什么? 如果你的团队已经在用 Zod(现在前端用 Zod 的比例越来越高),前后端终于可以共用一套 schema 了,不用在 class DTO 和 Zod schema 之间反复翻译。对于新项目,这直接减少了一层抽象。

原生可观测性:@nestjs/observe 来了

NestJS 终于有了官方的可观测性方案。@nestjs/observe 通过 instrumentation 选项接入请求生命周期,自动追踪 HTTP、微服务、消息队列和后台任务,不用手动创建 span。

typescript 复制代码
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule, {
    instrumentation: true,
  });
  await app.listen(3000);
}
bootstrap();

这对我们意味着什么? 之前搞 OpenTelemetry 接入要么自己封装 middleware,要么用社区的 @autom instrumentation 之类的包,总归有点别扭。现在官方下场了,至少 MVP 阶段的可观测性可以开箱即用。

配置模块:Joi 不再是唯一选择

@nestjs/config 的验证层从 Joi 专属迁移到了 Standard Schema。现在你可以用 Zod 来验证配置文件了。如果继续用 Joi,需要升级到 v18+,并把特定配置项嵌套到库选项下。

对于已经大量使用 Joi 的项目,迁移成本不高但需要注意细节。新项目嘛,直接用 Zod 就完事了。

路由冲突检测

新增 routeConflictPolicy 应用级选项,帮你诊断路由遮蔽问题。这是个 opt-in 功能,不影响现有行为。

这对我们意味着什么? 大项目里路由越来越多,偶尔会碰到"明明注册了但就是命中不了"的诡异问题。这个功能相当于给路由注册加了一双眼睛,建议新项目和排查问题时都打开。

异常错误码

异常现在可以携带 errorCode

typescript 复制代码
throw new BadRequestException({
  errorCode: 'USER_ALREADY_EXISTS',
  message: '该用户名已被注册',
});

这对我们意味着什么? 以前客户端要根据错误做分支处理,只能解析 message 文本,既脆弱又受国际化影响。现在有了稳定的 errorCode,前端/客户端的错误处理可以写得更干净。这个改动不大但很实用。

日志增强

ConsoleLogger 现在会把连续的普通对象归入同一条日志,JSON 模式下自动嵌套为结构化参数。小改进,但对日志采集场景很友好。


CLI 大换血

CLI 被从零重写了------ESM 模块、Vitest 测试、端到端测试、类型化上下文。不只是内部重构,用户体验也有明显变化:

新命令:

  • nest update:一键自动迁移,处理依赖版本、配置调整、库替换等机械性操作。加 --dry-run 可以预览而不实际修改
  • nest deploy:对接 Mau 平台的部署命令

工具链大洗牌:

  • Rspack 替代 Webpack,成为 monorepo 默认打包器
  • oxlint 替代 ESLint,新项目默认使用
  • Vitest 替代 Jest,新建 ESM 项目默认测试运行器(底层用 OXC 编译器处理 TypeScript 装饰器元数据)
  • Bun 正式加入支持的包管理器列表

其他变化:

  • 装饰器生成器改用 Reflector.createDecorator()
  • 新增构建选项:--parallel(并行构建)、--declarations(输出声明文件)、--silent(静默模式)

说实话,从 Webpack → Rspack、ESLint → oxlint、Jest → Vitest 这套工具链迁移,能感受到 NestJS 团队在往"更快、更现代"的方向全速推进。对老项目来说,这些工具切换不是强制的,但新项目会直接享受红利。


破坏性变更:升级前必看

生命周期钩子执行顺序变了

onModuleInitonModuleDestroy 等钩子现在基于组件层级执行,不再是之前的扁平顺序。如果你的模块之间有依赖特定初始化顺序的逻辑(比如数据库连接 → 缓存预热 → 消息队列注册),升级后务必仔细检查。

NATS 传输层换包了

NATS 集成切换到新的包,序列化方式改为 JSON 字符串。如果你在用 NATS,需要更新载荷读取方法。

GraphQL 两项变更

  • WebSocket 订阅从 subscriptions-transport-ws 切换到 graphql-ws,客户端必须同步更新
  • 默认 GraphQL IDE 从 Apollo Sandbox 切换到 GraphiQL

Pipe 签名泛型化

ArgumentMetadata 变成了泛型类型:

typescript 复制代码
// v11
transform(value: any, metadata: ArgumentMetadata) { ... }

// v12
transform(value: any, metadata: ArgumentMetadata<T>) { ... }

如果你写了自定义 Pipe,需要检查 transform 方法签名。

配置验证全面转向 Standard Schema

Joi 专属逻辑被移除,改走 Standard Schema。用 Joi 的需要升级版本并调整配置结构。


废弃与移除

  • Webpack 工作流:已废弃,推荐迁移到 Rspack
  • Angular 脚手架(Angular schematic):完全移除,不再随 CLI 分发

迁移三步走

第一步:更新全局 CLI

bash 复制代码
npm i -g @nestjs/cli@latest

第二步:运行自动迁移

bash 复制代码
nest update

这个命令会处理大部分机械性变更(依赖版本、配置格式、库替换等)。建议先 nest update --dry-run 预览一下再实际执行。

第三步:手动检查清单

自动化工具不覆盖的部分需要你手动过一遍:

  • 自定义 bootstrap 代码:模块格式变了,手动写的引导逻辑可能需要调整
  • 自定义打包器配置:如果有自定义 Webpack 配置,考虑迁移到 Rspack 或确认兼容
  • 生命周期钩子依赖:审查模块间初始化/销毁的先后关系
  • NATS 序列化代码:如使用 NATS,适配新的 JSON 字符串格式
  • GraphQL WebSocket 客户端 :如使用订阅,确保客户端切到 graphql-ws 协议
  • 自定义 Pipe 签名 :检查 ArgumentMetadata 泛型化是否需要改动

nest update 不会帮你做的事: CJS → ESM 迁移、Jest → Vitest 切换、ESLint → oxlint 替换。这三项需要你根据团队情况自行决策。


其他值得关注的改进

  • 自定义验证错误输出形状------终于可以控制验证错误的格式了
  • gRPC 状态码映射通过新异常过滤器修正
  • Kafka 消息模式支持正则表达式
  • WebSocket 网关支持请求作用域提供者
  • disconnect 处理器可接收断开原因
  • 微服务新增 pre-execution 生命周期钩子
  • Express 适配器支持优雅排空(graceful draining)
  • HTTP 错误映射在核心适配器层面全面重构

要不要现在升级?

新项目: 直接上 v12,没有历史包袱,ESM + Zod + Vitest 的全套现代工具链开箱即用。

存量项目: 建议先 nest update --dry-run 看看影响面。如果你没用 NATS、没用 GraphQL 订阅、没写自定义 Pipe,那大部分变更自动迁移就能搞定,升级风险不大。如果涉及这几项,建议留够半天到一天的时间来手动检查和测试。

不着急升级的场景: 如果你的项目深度依赖 Webpack 自定义配置、Joi 验证和 Jest 测试,这些都不会在 v12 里坏掉(只是标记废弃),可以按自己的节奏安排迁移计划。


参考链接


觉得有帮助的话,点个赞收藏一下?有问题欢迎评论区讨论。

相关推荐
YIAN5 天前
NestJS 入门核心梳理:模块化架构、装饰器与依赖注入
后端·nestjs
半个落月6 天前
NestJS 入门实战:从工厂模式到 Todo CRUD,讲透模块化、依赖注入与测试
后端·nestjs
浮生望7 天前
NestJS MVC 实战:模块化架构、CRUD 全流程与标准化错误处理
nestjs
浮生望7 天前
NestJS 架构解密:从工厂模式到装饰器模式的企业级Node.js框架设计
nestjs
烬羽7 天前
Dockerfile 是"做奶茶的配方"?用 todos 全栈项目看懂 Docker 构建与发布
nginx·docker·nestjs
Liora_Yvonne8 天前
从数据库到 Vue 页面:用 LY Fullstack 完成第一个真实全栈业务模块
前端·后端·nestjs
倾颜11 天前
NestJS 核心概念梳理:从 Module、Controller 到 Guard、Interceptor
后端·node.js·nestjs
薛定谔的算法11 天前
NestJS:让 Node.js 后端告别「野路子」
后端·node.js·nestjs