6

API with NestJS #144. Creating CLI applications with the Nest Commander

 3 months ago
source link: https://wanago.io/2024/02/05/api-nestjs-cli-nest-commander/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

API with NestJS #144. Creating CLI applications with the Nest Commander

NestJS

February 5, 2024
This entry is part 144 of 144 in the API with NestJS

Command Line Applications are very useful for developers. We can interact with them by executing specific commands in the terminal, giving us much control and flexibility. You probably already interact with various CLI applications, such as Git or NPM.

Interestingly, we can also use NestJS to create a Command Line Interface (CLI) application. In this article, we learn what we can do with CLI apps and how to implement one with NestJS and the Nest Commander.

Command Line Interface applications

A straightforward example of a CLI tool is cp, which is used to copy files.

cp file.txt copiedFile.txt

Let’s break it down:

  • cp is the command name
  • file.txt copiedFile.txt are arguments. The first one is the file name we want to copy, and the second is the destination.

Many options are configurable through options.

cp -R directoryToCopy copiedDirectory

In the above example, -R is an option. It means we want to copy a folder with all its contents recursively.

In some cases, options can have their own arguments as well. For example, the following command makes a GET request and displays the result in the terminal:

curl https://www.google.com

However, we can use the -o option to store the result of the request in the file. If we do that, we need to provide an additional argument with the name of the output file.

curl -o output.html https://www.google.com

It is worth noticing that options are prefixed with a single dash if they use a shorthand or an abbreviated version of the option. On the other hand, options prefixed with two dashes are usually followed by a whole word or multiple words. For example, we can use --output instead of -o when using curl.

Creating CLI applications with NestJS

To create a CLI application with NestJS, let’s first create a new NestJS project.

npx @nestjs/cli new nestjs-cli

The @nestjs/cli is a CLI tool created by the NestJS team.

We also need to install the Nest Commander library.

npm install nest-commander

First, we need to adjust our bootstrap method in the main.ts file.

main.ts
import { AppModule } from './app.module';
import { CommandFactory } from 'nest-commander';
async function bootstrap() {
  await CommandFactory.run(AppModule);
bootstrap();

We can now create our first simple command. To do that, we need to create a class that extends the CommandRunner and has the run method.

get-date.command.ts
import { Command, CommandRunner } from 'nest-commander';
@Command({ name: 'get-date', description: 'Prints the date' })
export class GetDateCommand extends CommandRunner {
  async run() {
    const currentDate = new Date();
    console.log(currentDate.toLocaleDateString());

We mark the run method with async because it needs to return a promise.

Above, we create the current-date command that prints the current date.

We must also add our class to the providers array in our module.

app.module.ts
import { Module } from '@nestjs/common';
import { GetDateCommand } from './get-date/get-date.command';
@Module({
  providers: [GetDateCommand],
export class AppModule {}

Running our command

To test our command, we must build our NestJS application and run it with Node.js. We can simplify this process by creating an appropriate script in our package.json file.

package.json
  "scripts": {
    // ...
    "start": "nest build && node dist/main.js"
  // ...

We can now run the start script and provide the command name we want to run.

Screenshot-from-2024-02-03-15-27-40.png

Using arguments

Let’s change our code so that the get-date command accepts a date it should format.

get-date.command.ts
import { Command, CommandRunner } from 'nest-commander';
@Command({ name: 'get-date', description: 'Prints the date' })
export class GetDateCommand extends CommandRunner {
  parseDate(date?: string) {
    if (date) {
      return new Date(date);
    return new Date();
  async run(parameters: string[]) {
    const parsedDate = this.parseDate(parameters[0]);
    console.log(parsedDate.toLocaleDateString());

Any argument we pass to our command is available through the first argument in the run method.

There is a catch, though. Since we use npm to run our script, we must use the -- separator before providing the arguments.

Screenshot-from-2024-02-03-16-04-55.png

Using options

Let’s handle an option that signifies that we want to include the time in the output. To do that, we need to use the @Option decorator.

get-date.command.ts
import { Command, CommandRunner, Option } from 'nest-commander';
interface GetDateOptions {
  time?: boolean;
@Command({ name: 'get-date', description: 'Prints the date' })
export class GetDateCommand extends CommandRunner {
  parseDate(date?: string) {
    if (date) {
      return new Date(date);
    return new Date();
  formatDate(parsedDate: Date, shouldIncludeTime: boolean) {
    if (shouldIncludeTime) {
      return `${parsedDate.toLocaleDateString()} ${parsedDate.toLocaleTimeString()}`;
    return parsedDate.toLocaleDateString();
  async run(parameters: string[], options: GetDateOptions) {
    const parsedDate = this.parseDate(parameters[0]);
    const formattedDate = this.formatDate(parsedDate, options.time);
    console.log(formattedDate);
  @Option({
    flags: '-t, --time',
    description: 'Means that the output should include time',
  parseTimeOption() {}
Screenshot-from-2024-02-03-16-43-40.png

We can provide additional arguments related to our option. For example, let’s allow the users to provide the timezone offset they want to use with the date.

All arguments we pass to our commands are treated as strings. The @Option is a method decorator, and we can use that to parse the argument from a string to a number.

get-date.command.ts
import { Command, CommandRunner, Option } from 'nest-commander';
interface GetDateOptions {
  time?: boolean;
  timezoneOffsetInMinutes?: number;
@Command({ name: 'get-date', description: 'Prints the date' })
export class GetDateCommand extends CommandRunner {
  parseDate(date?: string) {
    if (date) {
      return new Date(date);
    return new Date();
  processTimezoneOffset(parsedDate: Date, timezoneOffset?: number) {
    if (timezoneOffset) {
      return new Date(parsedDate.getTime() + timezoneOffset * 60000);
    return parsedDate;
  formatDate(parsedDate: Date, shouldIncludeTime?: boolean) {
    if (shouldIncludeTime) {
      return `${parsedDate.toLocaleDateString()} ${parsedDate.toLocaleTimeString()}`;
    return parsedDate.toLocaleDateString();
  async run(parameters: string[], options: GetDateOptions) {
    const parsedDate = this.parseDate(parameters[0]);
    const dateWithTimezoneOffset = this.processTimezoneOffset(
      parsedDate,
      options.timezoneOffsetInMinutes,
    const formattedDate = this.formatDate(dateWithTimezoneOffset, options.time);
    console.log(formattedDate);
  @Option({
    flags: '-t, --time',
    description: 'Means that the output should include time',
  parseTimeOption() {}
  @Option({
    flags: '-z, --timezone-offset-in-minutes [timezoneOffsetInMinutes]',
    description: 'The timezone offset in minutes',
  parseTimezoneOffsetOption(value: string) {
    return Number(value);

Thanks to that, we can provide the number of minutes we want to add or subtract from the provided date.

Screenshot-from-2024-02-03-16-55-43.png

Summary

In this article, we’ve gone through how command-line interface applications work and how to control them with arguments and options. We also learned how to implement a CLI application using NestJS and the Nest Commander. With the right approach, command-line interface applications can be straightforward, practical, and valuable tools for developers.

Series Navigation<< API with NestJS #143. Optimizing queries with views using PostgreSQL and Kysely

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK