DEV Community

Cover image for How to Make a CLI App with NestJS: Step-by-Step
Ben Force
Ben Force

Posted on • Originally published at thebenforce.com on

How to Make a CLI App with NestJS: Step-by-Step

I recently started a role on a company's developer experience team. One of the responsibilities that comes along with this is building larger CLI applications.

As I wrote previously, I use the commander library when writing DevOps scripts for my NodeJS projects. This is great for smaller utilities that only a couple of people will use, but what about if it needs to interact with multiple services and have lots of nested commands?

As it turns out NestJS supports this using the same commander library that I used previously. In this article, I'll show you how to create a basic CLI using this setup.

Creating the Project

Creating the CLI project is the same as creating an API with NestJS. Simply follow the basic workflow on nestjs website.

npm i -g @nestjs/cli
nest new my-cli-project
cd my-cli-project
Enter fullscreen mode Exit fullscreen mode

Setup nest-commander

The nest-commander library provides all the code that you'll need to create your commander CLI in the background. Like every other node dependency, the first step is to install the package.

npm i nest-commander
Enter fullscreen mode Exit fullscreen mode

Creating a Command

Just like with normal nestjs development, we need to create a module that our command will live in.

Nest-Commander provides some tools that plug into the nest cli. I've tried using them but wasn't able to get them to work. You may have better luck, but I'll continue with the manual process.

nest g module CowSay
Enter fullscreen mode Exit fullscreen mode

Normally you would create a controller or resolver in your module using the nest cli. Since we're creating a cli, we want to create a Command instead. Create a cow-say.command.ts file in the src/cow-say folder and open it.

Each command that you create will extend the CommandRunner class and use the @Command() decorator. In the cow-say.command.ts file that you just created, add the following.

import { Command, CommandRunner } from 'nest-commander';

@Command({
  name: 'cowsay',
  options: {
    isDefault: true,
  },
})
export class CowSayCommand extends CommandRunner {
  async run(): Promise<void> {
  }
}
Enter fullscreen mode Exit fullscreen mode

To get this command to display something, import the cowsay library.

import * as cowsay from 'cowsay';
Enter fullscreen mode Exit fullscreen mode

You'll need to install it too...

npm i cowsay
Enter fullscreen mode Exit fullscreen mode

...and update the run method in cow-say.command.ts

async run(): Promise<void> {
  console.log(cowsay.say({ text: 'Hello World!' }));
}
Enter fullscreen mode Exit fullscreen mode

Wiring Everything Together

Now that the command is created, you need to register it as part of the module. Open the cow-say.module.ts file and add CowSayCommand as a provider.

import { Module } from '@nestjs/common';
import { CowSayCommand } from './cow-say.command';

@Module({
  providers: [CowSayCommand],
})
export class CowSayModule {}
Enter fullscreen mode Exit fullscreen mode

Tell NestJS that it's not a server

Now comes the tricky part. The nest project that you created is setup to create a REST API by default, but you don't need that. So delete the app service and controller.

rm app.service.ts
rm app.controller.ts
Enter fullscreen mode Exit fullscreen mode

Next, update the AppModule to import the CowSay module.

import { Module } from '@nestjs/common';
import { CowSayModule } from './cow-say/cow-say.module';

@Module({
  imports: [CowSayModule],
})
export class AppModule {}
Enter fullscreen mode Exit fullscreen mode

Finally, you need to update the main.ts file. You'll change the bootstrap function to use CommandFactory instead of NestFactory.

import { CommandFactory } from 'nest-commander';
import { AppModule } from './app.module';

async function bootstrap() {
  await CommandFactory.run(AppModule);
}
bootstrap();
Enter fullscreen mode Exit fullscreen mode

Running It

I like to have the option of running my CLI tools without having to run a TypeScript build step. This helps speed up development.

To run your CLI without building it, you'll be using the ts-node package. To get started install it as a development dependency.

npm i -D ts-node
Enter fullscreen mode Exit fullscreen mode

Now add a new script to package.json

"start:cli": "ts-node src/main.ts"
Enter fullscreen mode Exit fullscreen mode

...and you can test your CLI by running the script

❯ npm run start:cli

> my-cli-project@0.0.1 start:cli
> ts-node src/main.ts

 ______________
< Hello World! >
 --------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||
Enter fullscreen mode Exit fullscreen mode

Summary

This article showed how to get started building your very first NestJS CLI application. Taking this approach can help you build a larger application that is maintainable. In future articles I'll be introducing you to more advanced features of this setup.

Top comments (0)