How to Create Node.js CLI with Yargs

How to Create Node.js CLI with Yargs

Since Node.js provides us with all utilities to build a cli app, why should you use yargs ? A good example is better than a lot of explanations.

As developers, we use cli tools everyday. We use them to simplify common tasks of our job:

  • Packaging
  • Linting
  • Building apps
  • Deploying apps
  • Publishing packages
  • Automate a lot of stuff...

But that's not all. A lot of them aren't related to development at all! Here is a list of cli apps.

I developped myself a couple of cli tools like gitmoji-changelog. It is a changelog generator for gitmoji commit convention. I also contributed to gatsby-cli which helps developers build blazing fast websites and apps using React. All of these were made with yargs.

Why using yargs?

Since Node.js provides us with all utilities to build a cli app, why should you use yargs?

A good example is better than a lot of explanations. Let's go through the creation of a simple cli app. When called it will display Hello world!.

The cli take an argument to override world word. It also takes a named option times to log the message more than once. We will build it step by step without using yargs then refactor the codebase using it.

First of all, we create an index.js file with the following content.

console.log('Hello world!')

We execute our file using node and our message is printed in our console.

[email protected]:~$ node index.js
Hello world!

Fine, arguments are available in the argv property of the global variable process. The first one is the executable path and the second one the path to the JavaScript file that was executed.

[
  "~/.nvm/versions/node/v10.15.3/bin/node",
  "~/index.js"
]

If we call the cli with an argument, it will the third element of this array. We get its value writing process.argv[2] and using world as default value if it isn't provided.

const args = process.argv

const name = args[2] || 'world'

console.log(`Hello${name}!`)

Call the cli, you can now override world!

[email protected]:~$ node index.js you
Hello you!

Things will go wild! Remember we want to add an option to display the message more than one time. Optional arguments are usually represented like this --times 3. They can be placed where you want.

We begin by dealing with the case the optional argument is placed after the name argument.

const args = process.argv

const name = args[2] || 'world'
const times = args[4] || 1

for (let i = 0;i < times; i++) {
  console.log(`Hello${name}!`)
}

Call the cli, now the message is displayed three times!

[email protected]:~$ node index.js you --times 3
Hello you!
Hello you!
Hello you!

The previous code won't work if we don't provide the name argument. It won't work either if you place the optional argument before the name.

We change the code to handle the use case when the optional argument is placed in first position.

// ...
if (args[2] === '--times') {
  name = args[4]
  times = args[3]
} 
// ...

We keep the same behavior when placed after the name.

// ...
} else if (args[3] === '--times') {
  name = args[2]
  times = args[4]
}
// ...

Here is the case where the name argument is provided and the optional argument isn't.

// ...
} else if (args[2] && args[2] !== '--times') {
  name = args[2]
}
// ...

Here is the final code.

const args = process.argv
let times = 1
let name = 'world'

if (args[2] === '--times') {
  name = args[4]
  times = args[3]
} else if (args[3] === '--times') {
  name = args[2]
  times = args[4]
} else if (args[2] && args[2] !== '--times') {
  name = args[2]
} 

for (let i = 0;i < times; i++) {
  console.log(`Hello ${name}!`)
}

It is a bit complex and hard to read. Moreover it won't work if we add a new positional argument.

Refactor our cli app using yargs

To build a maintainable and scalable cli app we will use yargs. It exposes a lot of functions well described in its documentation. We will use the function command. It takes four parameters, a name, a description, a builder and a handler. If you pass * or $0 as name parameter it will be the default command.

require('yargs')
  .command('$0 [name]', 'start the server',() => {}, () => {
    console.log('Hello world!')
  })

The code is a bit more complex to only display a Hello world! message. It will become more interesting as our code becomes more complex. Let's add our name argument. It will be done in the builder parameter which is a function that gets yargs instance as parameter. We use the positional function to describe our argument. As you can see, it directly takes a default value.

require('yargs')
  .command('$0 [name]', 'start the server',(yargs) => {
    yargs
      .positional('name', {
        describe: 'name to display',
        default: 'world'
      })
  }, () => {
    console.log(`Hello world!`)
  })

Arguments are passed as parameter to the handler function. It is an object with a property for each argument. We named our argument name, its value is available in argv.name property.

require('yargs')
  .command('$0 [name]', 'start the server',(yargs) => {
    yargs
      .positional('name', {
        describe: 'name to display',
        default: 'world'
      })
  }, (argv) => {
    console.log(`Hello ${argv.name}!`)
  })

Time to see the power of yargs. We add our optional argument times using the option function which has a similar API to positional. We don't forget to add a default value. The for is the same as in the vanilla implementation.

require('yargs')
  .command('$0 [name]', 'start the server',(yargs) => {
    yargs
      .positional('name', {
        describe: 'name to display',
        default: 'world'
      })
      .option('times', {
        alias: 't',
        type: 'number',
        default: 1,
        description: 'number of times the message is logged'
      })
  }, (argv) => {
    for (let i = 0;i < argv.times; i++) {
      console.log(`Hello ${argv.name}!`)
    }
  })

As you can see, we didn't have to deal with the technical complexity of writing a cli app. yargs handles it for us.

Bonus: It comes with help option

yargs automatically adds a command help for you! It uses the information we provided when we described our interface.

[email protected]:~$ node index.js --help
yargs.js [name]

start the server

Positionals:
  name  name to display                                        [default: "world"]

Options:
  --help       Print the help                                       [boolean]
  --version    Print the version number                            [boolean]
  --times, -t  number of times the message is logged        [number] [default: 1]

yargs's API is well documented and you can find more complex examples in it.

Now you can build all the cli apps you ever imagined!

Thank you for reading !

Nodejs Javascript Webdev Tutorial

Bootstrap 5 Complete Course with Examples

Bootstrap 5 Tutorial - Bootstrap 5 Crash Course for Beginners

Nest.JS Tutorial for Beginners

Hello Vue 3: A First Look at Vue 3 and the Composition API

Building a simple Applications with Vue 3

Deno Crash Course: Explore Deno and Create a full REST API with Deno

How to Build a Real-time Chat App with Deno and WebSockets

Convert HTML to Markdown Online

HTML entity encoder decoder Online

JavaScript Tutorial: if-else Statement in JavaScript

This JavaScript tutorial is a step by step guide on JavaScript If Else Statements. Learn how to use If Else in javascript and also JavaScript If Else Statements. if-else Statement in JavaScript. JavaScript's conditional statements: if; if-else; nested-if; if-else-if. These statements allow you to control the flow of your program's execution based upon conditions known only during run time.

JavaScript Snake Game Tutorial - Develop a Simple Snake Game

JavaScript Snake Game - Develop simple snake game with javascript and html. In this step by step guide we provided all the instructions to develop the game easily. Snake game is an interesting JavaScript project for beginners. Snake game is a single-player game, which we’ve been playing for a very long time.

JavaScript Tutorial 50 - Promises in JavaScript

JavaScript Tutorial 50 - Promises in JavaScript - This tutorial describes how to create and handle promises in JavaScript.

Hire NodeJs Developer

Looking to build dynamic, extensively featured, and full-fledged web applications? **[Hire NodeJs Developer](https://hourlydeveloper.io/hire-dedicated-node-js-developer/ "Hire NodeJs Developer")** to create a real-time, faster, and scalable...

NodeJS Beginner Tutorial - Learn To Build REST APIs in JavaScript - [Full Course]

This is a full Node JS course! In this course you will learn what Node is and how to use it. More importantly, we aren't just going to teach you Node in-depth with no apparent purpose, we are going to teach you how to build REST APIs with Node. Node Beginner Tutorial - Learn To Build REST APIs In JavaScript - [Full Course]