nestjs-console is a module that provide a cli. A ready to use service class for your modules that exposes methods to register commands and sub commands using the npm package commander
Why
The nestjs framework is missing a cli to access the application context.
Common use case : Headless application, cront task, export data, etc...
nestjs-console provide a way to bind cli command and subcommands to providers's methods.
How it works
The console service works as a standalone process, like the classic entry point, and will initialize a NestApplicationContext (headless) instead a NestApplication.
The console service will be accessible inside the container.
- Bootstrap (entry point e.g console.ts) is invoked by cli.
- Create a headless nest app
- Any module inside the app can create command and subcommands using nestjs-console with commander
- nestjs-console invoke commander
- commander will do the rest.
npm install commander nestjs-console
yarn add commander nestjs-console
Note:
For commander <5.0.0, use nestjs-console@2.1.0
For commander >=5.0.0 (latest), use nestjs-console@^3.0.2
Create a cli endpoint
Create a file at root next to your entry point named console.ts
Import your app module or any module you want to be loaded. Usually this is your main nestjs module.
import { BootstrapConsole } from 'nestjs-console';
import { MyModule } from './module';
const bootstrap = new BootstrapConsole({
module: MyModule,
useDecorators: true
});
bootstrap.init().then(async (app) => {
try {
await app.init();
await bootstrap.boot();
process.exit(0);
} catch (e) {
process.exit(1);
}
});
Import the ConsoleModule in your main module
import { Module } from '@nestjs/common';
import { ConsoleModule } from 'nestjs-console';
import { MyService } from './service';
@Module({
imports: [
ConsoleModule
],
providers: [MyService]
exports: [MyService]
})
export class MyModule {}
You can now inject the ConsoleService inside any nestjs providers, controllers...
There are 2 ways of registering providers methods to the console.
Using @decorators or using the ConsoleService.
Example of cli stack
Cli -> Command_A -> [
Command_A1 -> execution,
Command_A2 -> execution
]
-> Command_B -> [
Command_B1 -> execution,
Command_B2 -> [
Command_B2_a -> execution
Command_B2_b -> [... more sub commands ...]
]
]
-> Command_C -> execution
Api
As a simple example, we will define a cli with 2 commands (new and list), one of the command (new) will have 2 sub commands (directory and file)
Cli -> list -> -> execution,
-> new -> [
directory -> execution,
file -> execution
]
How to use Decorators
Registering methods using class decorators is very easy.
Nestjs providers that are decorated with @Console will be scanned and each member method that is decorated with @Command will be registered on the cli.
import { Console, Command, createSpinner } from 'nestjs-console';
@Console()
export class MyService {
@Command({
command: 'list <directory>',
description: 'List content of a directory'
})
async listContent(directory: string): Promise<void> {
const spin = createSpinner();
spin.start(`Listing files in directory ${directory}`);
const files = await new Promise((done) => setTimeout(() => done(['fileA', 'fileB']), 1000));
spin.succeed('Listing done');
console.log(JSON.stringify(files));
}
}
Register a command with sub commands
By default, the @Console will tell the module to register all decorated methods at root of the cli.
Example of Usage: [options] [command]
You can name your provider to be registered in a group command container.
This is useful when you have a lot of commands and you want to group them as sub command. (git style)
To achieve this, you have to group your methods into class.
You have to pass options to the @Console decorator to configure the name of the parent cli.
Decorated methods of the providers will be registered as a sub command instead of being registered at root.
@Console({
name: 'new',
description: 'A command to create an item'
})
export class MyNewService {
@Command({
command: 'file <name>',
description: 'Create a file'
})
async createFile(name: string): void | Promise<void> {
console.log(`Creating a file named ${name}`);
}
@Command({
command: 'directory <name>',
description: 'Create a directory'
})
async createDirectory(name: string): void | Promise<void> {
console.log(`Creating a directory named ${name}`);
}
}
If you need to register other sub commands from other Class to the same cli container, you have to decorate your class using the @Console decorator with the same name.
@Console({
name: 'new'
})
export class MyOtherService {...}
Example of Usage: new [options] [command]
How to use the ConsoleService
Registering methods using the ConsoleService is more flexible than decorators.
When you use the ConsoleService, you simply bind your methods to the cli manually.
This is useful if you need to create the cli or a part of the cli at runtime.
This way you can also create multiple commands ans sub commands from the same context.
import { Injectable } from '@nestjs/common';
import { ConsoleService } from 'nestjs-console';
@Injectable()
export class MyService {
constructor(private readonly consoleService: ConsoleService) {
const cli = this.consoleService.getCli();
this.consoleService.createCommand(
{
command: 'list <directory>',
description: 'description'
},
this.listContent,
cli
);
const groupCommand = this.consoleService.createGroupCommand(
{
name: 'new',
description: 'A command to create an item'
},
cli
);
this.consoleService.createCommand(
{
command: 'file <name>',
description: 'Create a file'
},
this.createFile,
groupCommand
);
this.consoleService.createCommand(
{
command: 'directory <name>',
description: 'Create a directory'
},
this.createDirectory,
groupCommand
);
}
listContent = async (directory: string): void | Promise<void> => {
console.log(`Listing files in directory ${directory}`);
};
createFile = async (name: string): void | Promise<void> => {
console.log(`Creating a file named ${name}`);
};
createDirectory = async (name: string): void | Promise<void> => {
console.log(`Creating a directory named ${name}`);
};
}
Add scripts in your package.json (if you want to use them)
{
"scripts": {
"console:dev": "ts-node -r tsconfig-paths/register src/console.ts",
"console": "node dist/console.js"
}
}
Usage
Call the cli (production)
node dist/console.js --help
npm run console -- --help
yarn console --help
Call the cli from sources (dev)
ts-node -r tsconfig-paths/register src/console.ts --help
npm run console:dev -- --help
yarn console:dev --help
Example of Response
Usage: console [options] [command]
Options:
-h, --help output usage information
Commands:
list <directory> List content of a directory
new A command to create an item