NestJS 作为一个高效、可扩展的 Node.js web 框架,其设计上受到 Angular 的启发,提供了强大的模块化与依赖注入特性。前端的开发者们早已习惯于这些概念,并且这种设计哲学也在 Node.js 的后端开发中大放异彩。
今天,我们要介绍的是 NestJS 中一个不那么广为人知,但却异常强大的装饰器:@Command
。这是 NestJS CLI 的一个特性,允许你创建自定义的命令行指令 (CLI commands),它可以极大地增强你的后端服务的可交互性和灵活性。
在开始之前,请确保你已经安装好了 NestJS CLI。如果你还没有安装,可以通过以下命令进行安装:
npm i -g @nestjs/cli
确认你的 NestJS 项目创建完成,并且可以运行。
@Command
装饰器是 @nestjs/cli
包的一部分。这个装饰器可以标记一个类方法作为一个可执行的 CLI 命令。这样,你就能在 NestJS 的上下文中运行任意代码,从而可以访问你应用中的所有服务与模块。
让我们步入正题,现在,我们要创建一个简单的命令,它将用于输出 “Hello World”。
首先,你需要一个处理器(handler)来处理你的命令。在你的项目中,创建一个 commands
目录,并在其中创建一个 hello-world.command.ts
文件。
mkdir src/commands
touch src/commands/hello-world.command.ts
在这个文件中,我们将创建一个类,然后使用 @Command
装饰器来标记它的一个方法作为 CLI 命令:
import { Command } from '@nestjs/cli';
export class HelloWorldCommand {
@Command({
command: 'hello',
describe: '输出 Hello World',
autoExit: true
})
run() {
console.log('Hello World');
}
}
你需要在 AppModule 或者相应的模块中注册这个命令。通常,你可以在 AppModule 的 providers
数组中加入你的命令类:
import { Module } from '@nestjs/common';
import { HelloWorldCommand } from './commands/hello-world.command';
@Module({
providers: [HelloWorldCommand],
})
export class AppModule {}
注册完成后,你可以通过 NestJS CLI 运行这个命令。打开终端,确保你处于 NestJS 项目的根目录中,然后运行:
nest hello
如果一切顺利,控制台应该会输出 “Hello World”。
@Command
装饰器还允许你定义额外的命令行参数与选项。例如,让我们扩展上面的示例,使得我们的命令可以接受一个名为 --name
的选项:
import { Command, Positional } from '@nestjs/cli';
export class HelloWorldCommand {
@Command({
command: 'hello [name]',
describe: '根据提供的名称输出 Hello',
autoExit: true
})
run(
@Positional({
name: 'name',
describe: '你要问候的人的名字',
type: 'string',
})
name: string,
) {
if (name) {
console.log(`Hello ${name}`);
} else {
console.log('Hello World');
}
}
}
运行命令并带上参数试试:
nest hello John
你应该会看到输出:
Hello John
NestJS 的 @Command
装饰器使用非常灵活,除了可以接受参数,你还可以进行更多的自定义和功能扩展。例如,接下来我们将展示如何使用选项和参数验证。
假设我们想给 ‘hello’ 命令添加一个布尔选项 --loud
,以控制问候语是否需要大声说出。这时候我们可以使用 @Option
装饰器来定义这个选项:
import { Command, Option } from '@nestjs/cli';
export class HelloWorldCommand {
@Command({
command: 'hello [name]',
describe: '根据提供的名称输出 Hello',
autoExit: true
})
run(
@Positional({
name: 'name',
describe: '你要问候的人的名字',
type: 'string',
})
name: string,
@Option({
name: 'loud',
describe: '放声大叫',
type: 'boolean',
})
loud?: boolean,
) {
let greeting = `Hello ${name || 'World'}`;
if (loud) {
greeting = greeting.toUpperCase();
}
console.log(greeting);
}
}
这样,如果你想“大声”地打招呼,你只需要运行:
nest hello John --loud
为了确保命令行参数和选项的正确性,我们可以添加一些验证逻辑。NestJS 允许你使用类验证器 (如 class-validator) 对参数和选项进行验证,帮助保障输入的严谨性。
首先,安装 class-validator 以及 class-transformer:
npm install class-validator class-transformer
然后我们可以创建一个 DTO (Data Transfer Object) 来表示我们的选项,并在其中使用 class-validator 提供的装饰器:
import { IsNotWhiteSpace, MinLength } from 'class-validator';
export class HelloCommandOptions {
@MinLength(1, {
message: '名称至少包含一个字符。',
})
@IsNotWhiteSpace({
message: '名称不能只包含空白字符。',
})
name: string;
@Option({
name: 'loud',
describe: '放声大叫',
type: 'boolean',
})
loud?: boolean;
}
现在,更新我们的 HelloWorldCommand 类以利用这些验证规则:
import { Command } from '@nestjs/cli';
import { validate } from 'class-validator';
import { plainToClass } from 'class-transformer';
import { HelloCommandOptions } from './hello-command-options';
export class HelloWorldCommand {
@Command({
command: 'hello [name]',
describe: '根据提供的名称输出 Hello',
autoExit: true
})
async run(
@Positional({
name: 'name',
describe: '你要问候的人的名字',
type: 'string',
})
name: string,
// 其余参数已经移动到 DTO 中
) {
const options = plainToClass(HelloCommandOptions, {name});
const errors = await validate(options);
if (errors.length > 0) {
console.error('Validation error:', errors);
return;
}
let greeting = `Hello ${options.name || 'World'}`;
if (options.loud) {
greeting = greeting.toUpperCase();
}
console.log(greeting);
}
}
通过这样的方式,我们不仅实现了 CLI 命令的参数与选项的解析,还引入了参数验证机制,使得我们的命令更加健壮和错误容错。
通过使用 NestJS CLI 和 @Command
装饰器的搭配,可以带来如下几点优势:
@Command
装饰器的设计给予了开发者巨大的灵活性,你可以轻松地为现有命令添加额外的参数和选项,或者定义全新的命令来满足你的需求。通过这篇教程,你已经学到了如何在 NestJS 中使用 @Command
装饰器来创建自定义的命令行接口,包括命令的基本创建,参数与选项的定义,以及参数的验证。NestJS 的 CLI 工具不仅可以为用户提供丰富的交互操作,也使得后端服务的维护和数据处理变得更为便捷。
确保在你的 NestJS 应用中充分利用 @Command
装饰器,你可以构建出强大且易于维护的应用。例如,你可以用它来创建数据迁移、执行数据库备份、生成报告,或者任何你可以想象到的与你的应用相关的脚本任务。