Easy CLI with NodeJS

| 3 min read

What is a CLI?

To start with some definition, CLI stands for command-line interface.

This is what you use every day with git, curl or other common commands, they are all CLI tools. They are running through what is commonly called a terminal, or a shell.

If you are not too familiar with "building CLI" or even the terminal, it might look like a black box for you. You might even think: "No way I'm going to learn bash, or whatever it is called, to build these tools".

The good news is: you do not need bash to build a CLI tool! Even though I like bash.

You can build CLI tools with a lot of different languages, like: go, python, C++ and even... javascript! NodeJS is great to build CLI tools.

All the following commands will be executed in a Unix Shell (bash).

Here is my favourite NodeJS CLI, type the following in your terminal:

npm install -g cowsay
cowsay "I'm running in NodeJS"

Which will output:

< I'm running in NodeJS >
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||

A little trick, you can also run it without installing it, using npx:

npx cowsay "I'm running in NodeJS"

Get started building a CLI

To create a CLI with NodeJS, there are a few pieces to put together.

Execute a node script

The first part is the be able to execute a node script.

A common way

  1. Create a file
  2. Write some content
  3. Execute it with NodeJS
touch my-script.js
echo 'console.log("Hello");' > my-script.js
node my-script.js

A more elegant way

  1. create a file
  2. Tell the shell in which environment it should run the script (shebang)
  3. Make the file executable with chmod
  4. Execute the file without specifying NodeJS
touch my-script.js
echo '#!/usr/bin/env node' > my-script.js
echo 'console.log("Hello");' >> my-script.js
chmod +x my-script.js

You can see the same type of file in the cowsay repo: cli.js.

Execute the script as a binary

The second part is to transform this script into a command line.

There is an easy way to do that by adding the script to a bin property of a package.json:

"bin": {
"my-cli": "./my-script.js"

There is a little bit of magic around this: when this package gets installed, npm will create symlinks for the scripts in a bin folder so that your shell can find the "binaries".

For example, when you install cowsay, the terminal outputs:

/usr/local/bin/cowthink -> /usr/local/lib/node_modules/cowsay/cli.js
/usr/local/bin/cowsay -> /usr/local/lib/node_modules/cowsay/cli.js
+ cowsay@1.5.0
added 41 packages from 10 contributors in 3.028s

These are the symlinks created and they are now ready to use in your terminal.

A little trick for local development, in the folder where the package.json is located, you can simply run:

npm link

This command will also create the symlinks needed. They can be removed with:

npm unlink

Create a compliant CLI

Now you can execute a CLI you made yourself, you can simply write some NodeJS code. You create and modify some files, parse some JSON files, make some asynchronous requests to an API like Github, upload some files to an AWS S3, and so on.

You can now build your own tools, make them do what you need, and execute them easily.

What might seem less easy to do, is to write a proper Unix CLI like others would expect, like:

my-cli -v
my-cli --version
my-cli --help
my-cli do-something
my-cli do-something --some-option

You need to think about writing help output, version output, subcommands, options that can be chained, with short name options, and so on. And of course in a format that would make all your heavy Unix CLI users happy.

Here is a guideline: clig.dev.

Fortunately, some packages provide really good boilerplates for your CLI tools, for example, commander.

Some npm packages will help you to make your CLI pretty and readable with some colours like chalk, while others will help you display complicated tables easily: cli-table3.

Distribution and usage

To distribute your CLI, you can simply publish your package to the npm registry. This is will make it available to everyone, don't forget to write some tests and some thorough documentation!

In terms of usage, you can simply install it:

npm install -g my-cli

Another scenario is if you do not want to publish the npm package because you want to keep the CLI in a private repo, you can simply clone your repo and execute the following:

npm install
npm link

It is also possible to execute the command without installing it before, for example on a CI, by using npx and targeting the package:

npx -p my-npm-package my-cli --help