How to Prototype a CLI Using CommanderJS and InquirerJS in Less Than 3 Hours

Amy Lily
The Startup
Published in
6 min readOct 14, 2020

In this tutorial, I want to help you create a very quick and dirty prototype to collect feedback for the CLI concepts that you want to test out quickly, fail fast, and move on to the next solution.

In the past few months, I was tasked to redesign the CLI experience for one of our products at Canonical (Ubuntu), MAAS. Feel free to check out my MAAS CLI prototype.

Before we start, I am assuming that you have npm and node installed on your local machine.

My current environment:

node version: v12.16.1
npm version: 6.13.4

In this tutorial I will show you how to create a CLI application for a bubble tea shop called macha.

The 2 main commands that our users can do:

  1. List everything in the menu:
$> macha list

2. Order something from the menu

$> macha order

Functionalities:

A user should have a choice to add toppings, select sugar levels, serve hot or cold, and select ice level. The result will show the total price of your order.

Let’s get started!

First of all let’s create a workspace and install a few packages.

Initialization:

$> mkdir macha && cd macha
$> npm init -y

Your output should look like this:

{
"name": "macha",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}

After you initialize the package, we need to install commanderJS, inquirerJS along with 2 npm modules: colors, to add colors to our CLI, and pad,to add padding to the outputs. These will come in handy afterwards.

$> npm install --save commander inquirer colors pad

Now that our project is initialized, we need to add a bin field into the package.json file. In the bin field, we need to add macha , because this is going to be our app name in the CLI.

{
"name": "macha",
"version": "1.0.0",
"description": "",
"main": "index.js",
"bin": {
"macha": "./bin/index.js"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}

Add 2 folders to your working directory ( bin and lib ):

You will need to create a bin folder and a lib folder in your working directory. Under bin is where index.js lives, this is where you will put all your commands and how you want to structure them. Under lib is where you store all you commands such as list and order . This folder handles all the command outputs. You will need one additional file where you store all your values, let’s name it menu_items.js.

|-macha
|-bin
|-index.js
|-lib
|-menu_items.js
|-list.js
|-order.js

|-node_modules
|-package.json

Create items in our menu menu_items.js:

This step, we are going to put all the items in our menu into one place menu_items.js . In this file, we need to export all the variables so we can use it in other modules.

Create your first command output list.js:

In the list.js file, you will import the tea variable from menu_items.js and customize how you want to show the output. I use pad and colors to make the space between outputs easily readable.

Create a list command in index.js:

In our index.js file, we will define the list command so when the prompt calls a list command, it will call the list.js file to show the output.

Now let’s try to run the command from your terminal.

In your macha directory, run:

~/macha> node bin/index.js

Your output should look like this:

Create a proper sub-command list :

Let’s create a proper list sub-command, the commanderJS way. We want to be able to run $> macha list from our command prompt, so we need to create list as a sub-command with CommanderJS.

The way it works is Commander will parse arguments in program.parse(arguments), which leaves any args not consumed by the program options in the program.args array.

In the example above, we need to add the Shebang line on the first line (as we will make it global later). Then create a list command with Commander. The convenient thing about this library is that, commanderJS will create a default help argument for you. There are different options and flavors you can choose from in commanderJS specific options, where you can choose how you want to customize your command.

Finally, add process.agrv in the last line, so Commander can parse user inputs.

Before we run, let’s install this CLI app globally so you can access this command from any prompt.

$> npm install -g ./

Now you can type macha list from any prompt.

Using InquirerJS to create interactive prompt for the order command:

For the order sub-command, I will show you a different approach. InquirerJS basically made it really easy for you to create an interactive command prompt, it accepts a list of questions where you can define its type: list rawlist expand checkbox confirm input number and password .

The structure of the answers are in a key/value hash. This will contain the answer you select in the prompt. There are also several plug-ins available for more fun stuff like inquirer-emoji, chalk-pipe, search-list, autocomplete, etc.

As a start, you need to create a set of questions.

In the example below, I created 2 questions sets. The first set of questions is the general question. However, If a user wants the tea cold, they need to answer one more question before seeing the total price.

In this example, the inquirer.prompt takes these list of questions and returns asPromise , which will be resolved when all questions are answered.

Because the answers I got from the inquirer prompt are in type: String, I needed to cast the variable into Float so I can sum the total price for tea + topping + milk.

total = parseFloat(answers.tea.split("$")[1]);

Your index.js file will now include the order sub-command.

Let’s try out the order command:

$> macha order

That sums up our tutorial for CommanderJS and InquirerJS.

In case you want to clone this project and play around with it, here is the code for macha-cli : https://github.com/amylily1011/macha-cli

Learning points:

  • When you apply colors to the output text and using PAD at the same time, they output may not align.
  • Inquirer has a default color setting, this is harder to override. When testing this out, make sure you communicate this with the user.
  • Although having colored output is a nice feature, but in reality, your users’ terminal might have a theme that does not support your color output. For instance, when you use normal blue on the text, it can be too dark and unreadable for certain terminal themes.

Bonus points for fancy add-ons:

If you want to create a CLI in style with spinners and progress bars, there are libraries like cli-progress, cli-table, cli-spinner, etc that can save you a lot of time creating cool stuff.

I hope this is helpful for UXers, Prototypers, and anyone who shares the love for prototyping. Feel free to reach out for any questions! Happy prototyping :).

--

--

Amy Lily
The Startup

UX Manager at Canonical | Open sourced ❤ | Cloud Technology | UX and Cooking.