Creating a CLI Tool with NestJS

@rnab
3 min readJan 13, 2025

--

NestJS is a powerful framework for building efficient and scalable Node.js server-side applications. Typically known for its robust server-side API development capabilities, NestJS can also be leveraged to create useful Command-Line Interface (CLI) tools. This guide will walk you through the steps of creating a CLI tool using NestJS and TypeScript.

Why NestJS for CLI?

NestJS, built with TypeScript, provides out-of-the-box support for TypeScript features and a modular architecture, which is conducive to creating maintainable and scalable CLI applications. Additionally, NestJS leverages IoC (Inversion of Control), making dependency management straightforward.

Setting Up a NestJS Project

First, you need to set up a NestJS project. If you haven’t installed the NestJS CLI yet, you can do so using npm or yarn:

npm i -g @nestjs/cli
# or
yarn global add @nestjs/cli

Create a new project:

nest new cli-tool

Navigate to the project directory:

cd cli-tool

Adding a CLI Command

NestJS doesn’t include a CLI-specific module, but with a little configuration, we can create a command-line tool. We’ll use Commander.js to handle CLI input. First, install commander:

npm install commander
# or
yarn add commander

Now, create a new commands directory in the src folder, and add a hello.command.ts file:

import { Command } from 'commander';
import { Injectable } from '@nestjs/common';

@Injectable()
export class HelloCommand {
run(name: string): void {
console.log(`Hello, ${name}! Welcome to your NestJS CLI tool.`);
}
}

// Initialize and configure the command
const helloCommand = new Command()
.name('hello')
.description('Greets the user')
.argument('<name>', 'Name of the user')
.action((name: string) => {
const command = new HelloCommand();
command.run(name);
});

export default helloCommand;

Bootstrapping the CLI

Next, we need to bootstrap the CLI application. Create a cli.ts file in the src directory:

import { Command } from 'commander';
import helloCommand from './commands/hello.command';

const program = new Command();

// Configure CLI metadata
program
.name('cli-tool')
.description('A simple CLI tool with NestJS')
.version('1.0.0');

// Add commands
program.addCommand(helloCommand);

// Parse the CLI arguments
program.parse(process.argv);

Running the CLI

For the CLI to execute, modify the package.json to add a CLI entry point:

{
"name": "cli-tool",
"version": "1.0.0",
"description": "A CLI tool built with NestJS",
"main": "dist/main.js",
"scripts": {
"start": "nest start",
"build": "nest build",
"cli": "ts-node src/cli.ts"
},
// Other configurations...
"bin": {
"cli-tool": "./dist/cli.js"
}
}

To execute the command:

npm run build
npm link # Links the CLI tool globally
cli-tool hello John # Outputs: Hello, John! Welcome to your NestJS CLI tool.

Extending Functionality

The architecture of NestJS allows for easily extending the CLI’s functionality. Create additional commands and services as you see fit. For instance, to add a ‘goodbye’ command:

  1. Add a new file goodbye.command.ts:
import { Command } from 'commander';
import { Injectable } from '@nestjs/common';

@Injectable()
export class GoodbyeCommand {
run(name: string): void {
console.log(`Goodbye, ${name}!`);
}
}

const goodbyeCommand = new Command()
.name('goodbye')
.description('Says goodbye to the user')
.argument('<name>', 'Name of the user')
.action((name: string) => {
const command = new GoodbyeCommand();
command.run(name);
});

export default goodbyeCommand;
  1. Update cli.ts to include the new command:
import { Command } from 'commander';
import helloCommand from './commands/hello.command';
import goodbyeCommand from './commands/goodbye.command';

const program = new Command();

program
.name('cli-tool')
.description('A simple CLI tool with NestJS')
.version('1.0.0');

program.addCommand(helloCommand);
program.addCommand(goodbyeCommand);

program.parse(process.argv);

Conclusion

You’ve now created a basic CLI tool using NestJS and TypeScript. While NestJS isn’t traditionally used for CLI tools, its modular and robust design makes it quite suitable for the task. From here, you can explore more advanced features such as complex argument parsing, dependency injection for more extensive functionalities, and more sophisticated commands.

Happy coding!

--

--

@rnab
@rnab

Written by @rnab

Typescript, Devops, Kubernetes, AWS, AI/ML, Algo Trading

No responses yet