Skip to content

Nest CLI 速查 ​

课程期间以及之后会用到的 @nestjs/cli 命令,都在 12.0.8 版本(TaskFlow 所用的版本)上验证过:这里提到的每个选项都能 在 nest <命令> --help 里找到,生成代码的示例都在 TaskFlow 里用 --dry-run 实际运行过。如果用的是其他版本,以 nest <命令> --help 的输出为准。

要理解 CLI 生成的东西(模块、provider、依赖注入),见 J1 · Nest CLI 和 J3 · 模块。

安装与查看信息 ​

bash
npm i -g @nestjs/cli                # 全局安装:得到 nest 命令
npx @nestjs/cli@latest new mon-api  # 不做全局安装
nest --version                      # CLI 版本
nest --help                         # 命令和 schematic 列表
nest generate --help                # 某个命令的选项
nest info                           # Node、CLI 以及项目中 @nestjs/* 包的版本

在项目里,npx nest … 和 npm 脚本用的是装在 devDependencies 里的 CLI,这样全局版本和项目版本就不会不一致。

创建项目:nest new ​

bash
nest new mon-api                    # 交互式提问(见下文)
nest new mon-api -p npm             # 包管理器:npm、yarn、pnpm……
nest new mon-api -g -s              # 不建 git 仓库(-g),不执行 npm install(-s)
nest new mon-api -d                 # dry run:只列出文件,不写入任何东西
nest new mon-api --no-observe       # 不询问是否安装 @nestjs/observe
选项简写作用
--package-manager <pm>-p直接指定包管理器,不再询问
--skip-git-g不执行 git init
--skip-install-s不安装依赖
--dry-run-d显示将要创建的文件
--language <lang>-lTypeScript(默认)或 JavaScript
--skip-tests-t不生成测试文件
--strictTypeScript 严格模式:CLI 12 中默认开启
--format用 Prettier 格式化生成的文件
--directory <dir>目标目录,可以和项目名不同
--observe / --no-observe直接配置或跳过 @nestjs/observe,不再询问

CLI 12 的提问 ​

  1. 包管理器(使用 -p 时不问)。
  2. 模块系统:ESM (ES Modules) [ with vitest ](默认选项)或 CJS (CommonJS) [ with jest ]。计划和 TaskFlow 用的是 CJS + Jest:请选它。这一项没有对应的命令行选项:nest new --type cjs 会被拒绝(unknown option)。
  3. NestJS Observe(遥测,付费服务),使用 --observe / --no-observe 时不问。

没有交互式终端时,得到的是 ESM + Vitest

在没有 TTY 的环境里运行(脚本、CI、< /dev/null),nest new 不提任何问题,直接采用默认值:生成的项目里有 vitest.config.ts,没有 Jest 配置。要得到 CommonJS + Jest 版本,请在终端里手动创建项目。

生成的文件(CJS + Jest 版本,也就是 TaskFlow 用的版本):src/(main.ts、app.module.ts、app.controller.ts、 app.service.ts、app.controller.spec.ts)、test/(app.e2e-spec.ts、jest-e2e.json)、nest-cli.json、 tsconfig.json、tsconfig.build.json、jest.config.ts、.oxlintrc.json(12 版脚手架的 lint 工具是 oxlint)、 .prettierrc、package.json。每个文件的细节和作用见 J1 · Nest 项目结构。

生成代码:nest g ​

bash
nest g <schematic> <名称> [路径] [options]     # 即 nest generate,别名 g
nest g service users                           # src/users/users.service.ts + spec
nest g module users/admin                      # src/users/admin/admin.module.ts

名字里可以带路径:users/admin 会创建 src/users/admin/。文件名采用 kebab-case,并带上 schematic 对应的后缀 (.service.ts、.guard.ts、.dto.ts 等):这正是仓库结构里介绍的约定。

Schematic 与别名 ​

Schematic别名生成内容是否注册到模块?
modulemo模块是,加入父模块的 imports
controllerco控制器 + spec是,加入最近模块的 controllers
servicesservice + spec是,加入最近模块的 providers
providerprprovider(文件名不带后缀)+ spec是,providers
resourceres模块、控制器、service、DTO、实体是,模块被导入父模块
gatewaygaWebSocket gateway是,providers
resolverrGraphQL resolver是,providers
guardguguard(守卫)+ spec否:用 @UseGuards() 或 APP_GUARD
interceptoritc拦截器 + spec否:用 @UseInterceptors() 或 APP_INTERCEPTOR
pipepi管道 + spec否:放进 @Param()、@Body(),或用 APP_PIPE
filterf异常过滤器 + spec否:用 @UseFilters() 或 APP_FILTER
middlewaremi中间件 + spec否:在 configure(consumer) 里注册
decoratord元数据装饰器 Reflector.createDecorator()(由 reflector.get() 读取)否
classcl类否
interfaceitf接口(.interface.ts)否
sub-appappmonorepo 中的应用见 monorepo
librarylibmonorepo 中的库见 monorepo
configurationconfignest-cli.json 文件—
applicationapplication新的工作区(nest new 用的就是它)—

TaskFlow 的装饰器是用 SetMetadata 和 createParamDecorator 写的(J8 · 公开路由); CLI 生成的 Reflector.createDecorator() 是元数据装饰器的带类型写法。

“最近的模块”是指:CLI 从目标目录开始沿目录树向上查找。在 TaskFlow 里,nest g mo users/admin 把 AdminModule 加进 UsersModule 的 imports,而不是 AppModule 的;还没有 orders 模块时,nest g co orders 会注册到 AppModule。

生成选项 ​

选项简写作用
--dry-run-d显示 CREATE / UPDATE,不写入任何东西:每次生成之前都先跑一遍
--flat / --no-flat不建 / 建立单独的子目录
--spec / --no-spec生成 / 不生成 .spec.ts 文件
--spec-file-suffix <s>测试文件使用 spec 以外的后缀
--skip-import不把生成的组件注册到模块
--project <名称>-pmonorepo 中的目标项目
--collection <名称>-c使用别的 schematic 集合
--format用 Prettier 格式化
--type <type>用于 resource:传输层(rest、graphql、microservice 等)
--crud用于 resource:生成 CRUD 入口

一整套 CRUD 资源 ​

bash
nest g resource orders                      # 提问:先选传输层,再问是否生成 CRUD 入口
nest g resource orders --type rest --crud   # 不提问(已验证)

在 TaskFlow 里的结果(dry run):

text
CREATE src/orders/orders.controller.spec.ts
CREATE src/orders/orders.controller.ts
CREATE src/orders/orders.module.ts
CREATE src/orders/orders.service.spec.ts
CREATE src/orders/orders.service.ts
CREATE src/orders/dto/create-order.dto.ts
CREATE src/orders/dto/update-order.dto.ts
CREATE src/orders/entities/order.entity.ts
UPDATE src/app.module.ts

update-order.dto.ts 继承 PartialType(CreateOrderDto),实体是一个空类:TypeORM 装饰器和校验要你自己加。生成资源 之后怎么继续,见 J2 · 生成资源。

用 CLI 搭出 TaskFlow 的骨架 ​

下面这些命令生成的文件和名字与 TaskFlow 一致(对过滤器和中间件做 dry run 时,正好撞上已有的文件,说明名字完全相同):

bash
nest g resource projects --type rest --crud
nest g resource tasks --type rest --crud
nest g mo users && nest g s users && nest g co users
nest g mo auth && nest g s auth && nest g co auth
nest g mo notifications
nest g mo health && nest g co health
nest g mo common
nest g f common/filters/all-exceptions --flat
nest g itc common/interceptors/logging --flat
nest g mi common/middleware/correlation-id --flat

TaskFlow 有几个文件是手工合并的,不是生成的:两个 guard 放在 auth/guards.ts,装饰器放在 common/decorators/auth.decorators.ts,健康检查的控制器和模块放在 health/health.controller.ts。见 仓库结构。

运行、构建、调试 ​

bash
nest start                          # 编译,然后运行 dist/main.js
nest start --watch                  # 每次修改后重新编译并重启(-w)
nest start -w --preserveWatchOutput # 两次重启之间保留控制台输出
nest start --debug --watch          # Node 调试器(--inspect),默认端口 9229
nest start --debug 0.0.0.0:9230     # 调试器的主机:端口
nest start --env-file .env.local    # 启动前加载一个 .env 文件
nest build                          # 编译到 dist/(开启 deleteOutDir 时先清空 dist/)
nest build --watch                  # 持续编译,但不运行

TaskFlow 的 npm 脚本沿用了脚手架里的那些:build(nest build)、start、start:dev(nest start --watch)、 start:prod(node dist/main),另外还有 J10 和 J5 中介绍的测试和迁移脚本。

选择编译器 ​

选项作用
--builder tsc (-b)TypeScript 编译器,默认值;TaskFlow 的 Swagger 插件就是用它验证的
--builder swc编译更快,但不做类型检查,除非加 --type-check(CLI 插件需要这项设置,见 SWC 官方文档)
--builder webpack / --builder rspack打成单个 bundle,配合 --webpackPath / --rspackPath
--webpack--builder webpack 的旧写法,已标记为弃用

Monorepo 与库 ​

nest new 没有 --monorepo 选项:普通项目在添加第一个应用时就变成了 monorepo。

bash
nest g app admin-api        # 把 src/ 和 test/ 移到 apps/<项目>/,并新增 apps/admin-api
nest g lib shared           # 生成 libs/shared;会询问导入前缀(默认 @app → @app/shared)
nest start admin-api --watch
nest build --all --parallel # 所有应用和库
nest g s orders -p admin-api

在 TaskFlow 里对 nest g app admin-api 做 dry run,会看到 DELETE src、DELETE test,然后是 CREATE apps/taskflow/src/…:这次转换会移动全部现有代码。请在单独的分支上做,而且只在多个应用确实需要共享代码时才做。 nest g lib 会更新 nest-cli.json、tsconfig.json(导入路径)、package.json 和 test/jest-e2e.json。

添加与升级 ​

bash
nest add @nestjs/swagger    # 安装这个包;如果它带有集成用的 schematic,一并运行
nest add <包名> -d        # dry run
nest upgrade                # 别名:nest update;迁移到 NestJS 最新的大版本
nest upgrade -d             # 应用之前先查看改动
nest upgrade -t next        # 使用某个 npm dist-tag
nest deploy                 # 部署到 Mau 云(NestJS 团队的服务)

TaskFlow 没有用 nest add:它的依赖在 package.json 里锁定到确切版本,这样本站引用的就是实际运行的代码。

nest-cli.json ​

TaskFlow 的这个文件:

json
{
  "$schema": "https://json.schemastore.org/nest-cli",
  "collection": "@nestjs/schematics",
  "sourceRoot": "src",
  "compilerOptions": {
    "deleteOutDir": true,
    "plugins": [
      {
        "name": "@nestjs/swagger",
        "options": { "classValidatorShim": true, "introspectComments": true }
      }
    ]
  }
}
键作用
collectionnest g 使用的 schematic 集合(@nestjs/schematics)
sourceRoot源码目录(src)
entryFilenest start 运行的文件(默认是 main)
compilerOptions.deleteOutDir每次构建前清空 dist/
compilerOptions.plugins编译插件;这里是 @nestjs/swagger,它只读取 *.dto.ts 和 *.entity.ts 文件(J11)
compilerOptions.assets、watchAssets把非 TypeScript 文件(.graphql、.hbs 等)复制到 dist/
compilerOptions.builder默认编译器(tsc、swc、webpack、rspack)
generateOptionsnest g 的默认选项,例如 { "spec": false }
monorepo、projects由 nest g app 和 nest g lib 填写

陷阱 ​

  • 不知不觉成了 ESM:没有交互式终端时,nest new 会创建 ESM + Vitest 项目(CLI 的提问)。
  • 文件已存在:CLI 不会提示覆盖,而是直接失败,报 A merge conflicted on path "…"。生成之前先用 -d 检查。
  • guard、管道、过滤器、拦截器、中间件没有注册:文件生成了,但没有任何东西启用它。要你自己接上 (Schematic 与别名、J6 · 执行顺序)。
  • 父模块不对:在子目录里生成的组件会加入最近的模块,不一定是你想要的那个;--skip-import 则什么都不注册。 仔细看 dry run 输出里的 UPDATE。
  • 不带 --watch 的 nest start 在修改后不会重新编译:请用 npm run start:dev。
  • 调试端口被占用:nest start --debug 0.0.0.0:9230。
  • Swagger 插件没生效:它只读取 *.dto.ts / *.entity.ts 文件,而且只在 nest build / nest start 编译时起作用: 单独运行 tsc 或 ts-jest 都不会应用它(验证过的陷阱)。
  • 版本不一致:让 @nestjs/core、@nestjs/common 和 @nestjs/cli 保持在同一个大版本;nest info 会显示它们的 版本,nest upgrade -d 帮你准备迁移。