Nest CLI 速查
课程期间以及之后会用到的 @nestjs/cli 命令,都在 12.0.8 版本(TaskFlow 所用的版本)上验证过:这里提到的每个选项都能 在 nest <命令> --help 里找到,生成代码的示例都在 TaskFlow 里用 --dry-run 实际运行过。如果用的是其他版本,以 nest <命令> --help 的输出为准。
要理解 CLI 生成的东西(模块、provider、依赖注入),见 J1 · Nest CLI 和 J3 · 模块。
安装与查看信息
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
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> | -l | TypeScript(默认)或 JavaScript |
--skip-tests | -t | 不生成测试文件 |
--strict | TypeScript 严格模式:CLI 12 中默认开启 | |
--format | 用 Prettier 格式化生成的文件 | |
--directory <dir> | 目标目录,可以和项目名不同 | |
--observe / --no-observe | 直接配置或跳过 @nestjs/observe,不再询问 |
CLI 12 的提问
- 包管理器(使用
-p时不问)。 - 模块系统:
ESM (ES Modules) [ with vitest ](默认选项)或CJS (CommonJS) [ with jest ]。计划和 TaskFlow 用的是 CJS + Jest:请选它。这一项没有对应的命令行选项:nest new --type cjs会被拒绝(unknown option)。 - 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
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 | 别名 | 生成内容 | 是否注册到模块? |
|---|---|---|---|
module | mo | 模块 | 是,加入父模块的 imports |
controller | co | 控制器 + spec | 是,加入最近模块的 controllers |
service | s | service + spec | 是,加入最近模块的 providers |
provider | pr | provider(文件名不带后缀)+ spec | 是,providers |
resource | res | 模块、控制器、service、DTO、实体 | 是,模块被导入父模块 |
gateway | ga | WebSocket gateway | 是,providers |
resolver | r | GraphQL resolver | 是,providers |
guard | gu | guard(守卫)+ spec | 否:用 @UseGuards() 或 APP_GUARD |
interceptor | itc | 拦截器 + spec | 否:用 @UseInterceptors() 或 APP_INTERCEPTOR |
pipe | pi | 管道 + spec | 否:放进 @Param()、@Body(),或用 APP_PIPE |
filter | f | 异常过滤器 + spec | 否:用 @UseFilters() 或 APP_FILTER |
middleware | mi | 中间件 + spec | 否:在 configure(consumer) 里注册 |
decorator | d | 元数据装饰器 Reflector.createDecorator()(由 reflector.get() 读取) | 否 |
class | cl | 类 | 否 |
interface | itf | 接口(.interface.ts) | 否 |
sub-app | app | monorepo 中的应用 | 见 monorepo |
library | lib | monorepo 中的库 | 见 monorepo |
configuration | config | nest-cli.json 文件 | — |
application | application | 新的工作区(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 <名称> | -p | monorepo 中的目标项目 |
--collection <名称> | -c | 使用别的 schematic 集合 |
--format | 用 Prettier 格式化 | |
--type <type> | 用于 resource:传输层(rest、graphql、microservice 等) | |
--crud | 用于 resource:生成 CRUD 入口 |
一整套 CRUD 资源
nest g resource orders # 提问:先选传输层,再问是否生成 CRUD 入口
nest g resource orders --type rest --crud # 不提问(已验证)在 TaskFlow 里的结果(dry run):
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.tsupdate-order.dto.ts 继承 PartialType(CreateOrderDto),实体是一个空类:TypeORM 装饰器和校验要你自己加。生成资源 之后怎么继续,见 J2 · 生成资源。
用 CLI 搭出 TaskFlow 的骨架
下面这些命令生成的文件和名字与 TaskFlow 一致(对过滤器和中间件做 dry run 时,正好撞上已有的文件,说明名字完全相同):
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 --flatTaskFlow 有几个文件是手工合并的,不是生成的:两个 guard 放在 auth/guards.ts,装饰器放在 common/decorators/auth.decorators.ts,健康检查的控制器和模块放在 health/health.controller.ts。见 仓库结构。
运行、构建、调试
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。
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。
添加与升级
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 的这个文件:
{
"$schema": "https://json.schemastore.org/nest-cli",
"collection": "@nestjs/schematics",
"sourceRoot": "src",
"compilerOptions": {
"deleteOutDir": true,
"plugins": [
{
"name": "@nestjs/swagger",
"options": { "classValidatorShim": true, "introspectComments": true }
}
]
}
}| 键 | 作用 |
|---|---|
collection | nest g 使用的 schematic 集合(@nestjs/schematics) |
sourceRoot | 源码目录(src) |
entryFile | nest 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) |
generateOptions | nest 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帮你准备迁移。