Building awesome CLIs with JavaScript and Oclif
Tags: javascript, tutorial, cli, node
Published: 2019-04-23 19:14:45

---
title: Building awesome CLIs with JavaScript and Oclif
published: true
description: As a software developer you probably use some kind of CLI, and it feels so good when it is intuitive and friendly. Let's discover how we can build our own CLIs!
tags: javascript, tutorial, cli, node
cover_image: https://thepracticaldev.s3.amazonaws.com/i/jdn75us5d067sz5dno4s.jpg
---

*Originally published in [Streaver's blog](https://www.streaver.com/blog/posts/building-CLIs-with-javascript-and-oclif.html).*


## Let's first define a CLI

A quick Google search yields, of course, a [Wikipedia article](https://en.wikipedia.org/wiki/Command-line_interface) with the CLI definition:

> A command-line interface or command language interpreter (CLI), also known as command-line user interface, console user interface and character user interface (CUI), is a means of interacting with a computer program where the user (or client) issues commands to the program in the form of successive lines of text (command lines). A program which handles the interface is called a command language interpreter or shell (computing).

So, in a nutshell, a CLI is a program that can understand requests made by a user in the form of text and then act and execute code in response to that.

This kind of programs are very useful for many different use cases, from simple CLIs like the `cal` Bash tool that displays the current month, to extremely complex ones like `kubectl` for managing Kubernetes clusters.

Even if you don't use CLIs directly every day (which is very unlikely), you probably are being indirectly affected by them, for example, `git` is a CLI, `gcc` the GNU compiler, `create-react-app` a scaffolding CLI for generating React apps, and many more.

## How to build your own CLIs

Like many things in the tech world the answer is: "it depends". There are many ways to build them and all of them are probably valid in different contexts. In this case, I am going to explore how to build one with JavaScript and [Oclif: a Node.JS Open CLI Framework (by Heroku)](https://github.com/oclif/oclif), which includes a CLI for building CLIs :thinking:.

**DANGER**

From now on I will assume you are comfortable with JavaScript and the NPM ecosystem in general, if you are not, you will probably get a general idea, but I recommend you read something about that before starting :smiley:.

### Getting started with Oclif

In my opinion, building something is usually a great way to learn, so in this case, I did some brainstorming with [@flarraa](https://twitter.com/flarraa) and decided to build a "Copa Libertadores" CLI ([see Wikipedia](https://en.wikipedia.org/wiki/Copa_Libertadores)) .

The idea is to provide a set of commands that can retrieve and display information about the already played matches and upcoming ones for the "Copa Libertadores" championship.

**Let's dig in!**

The Oclif CLI, has two possible ways to generate CLI projects, one is `npx oclif single mynewcli` and the second one is `npx oclif multi mynewcli`, in this case, we are going to generate a multi-command CLI.

We would like our command to look like `libertadores games:all`, `libertadores games:past`, `libertadores games:upcoming` and this is consistent with Oclif's multi-command CLI generation.

#### Initializing the project

First, we initialize the project by doing:

```bash
npx oclif multi libertadores-cli
```

This will ask some questions and after that, it will install everything you need to start coding!

```
$ npx oclif multi libertadores
npx: installed 442 in 32.454s

     _-----_     ╭──────────────────────────╮
    |       |    │      Time to build a     │
    |--(o)--|    │  multi-command CLI with  │
   `---------´   │  oclif! Version: 1.13.1  │
    ( _´U`_ )    ╰──────────────────────────╯
    /___A___\   /
     |  ~  |
   __'.___.'__
 ´   `  |° ´ Y `

? npm package name libertadores-cli
? command bin name the CLI will export libertadores
? description A simple CLI to get information about "Copa Libertadores" right in your terminal
? author Federico Kauffman
? version 0.0.0
? license MIT
? Who is the GitHub owner of repository (https://github.com/OWNER/repo) streaver
? What is the GitHub name of repository (https://github.com/owner/REPO) libertadores-cli
? Select a package manager yarn
? TypeScript No
? Use eslint (linter for JavaScript) Yes
? Use mocha (testing framework) Yes
? Add CI service config circleci (continuous integration/delivery service)
```

I have selected some defaults I like and now you have a bunch of files and folders which will be our main structure for the project. Next enter the directory with `cd libertadores-cli`.

I am going to briefly explain what Oclif has generated for us:

```
.
├── README.md
├── bin
│   ├── run
│   └── run.cmd
├── package.json
├── src
│   ├── commands
│   │   └── hello.js
│   └── index.js
├── test
│   ├── commands
│   │   └── hello.test.js
│   └── mocha.opts
└── yarn.lock

5 directories, 9 files
```

Looking at the tree of files you can see the `bin` directory which contains the binaries to run the command on each platform (Unix/Windows).

You see the `src` folder with an `index.js` file which simply exports an internal Oclif package which will load the available commands, and those commands are defined in the files placed in the `src/commands` folder. By default, Oclif generates a `hello` command, let's run that and see what we have:

```
$ ./bin/run

A simple CLI to get information about "Copa Libertadores" right in your terminal

VERSION
  libertadores-cli/0.0.0 darwin-x64 node-v11.13.0

USAGE
  $ libertadores [COMMAND]

COMMANDS
  hello  Describe the command here
  help   display help for libertadores
```

If you run the `hello` sub-command you get:

```
$ ./bin/run hello

hello world from ./src/commands/hello.js
```

Last but not least, you have the `tests` folder where you will place all your tests, in fact, Oclif already created some tests, and we can run them with `npm run test` or `yarn test`.

#### Creating the first command

First, we can delete the `hello` command since we are not going to use it, simply delete the `src/command/hello.js` and `tests/commands/hello.test.js`.

Now we can use the Oclif CLI generator command, let's create the `games:all` command with:

```
npx oclif command games:all
```

This will create all the files needed for the command (including tests) and also will update the `README.md` file automatically to include the new command.

We are going to get the details for "Copa Libertadores" from [http://www.conmebol.com/es/copa-libertadores-2019/fixture](http://www.conmebol.com/es/copa-libertadores-2019/fixture), and we are going to use [puppeteer](https://github.com/GoogleChrome/puppeteer) to enter the site and get the data.

```
$ yarn add puppeteer --save
```

```js
const puppeteer = require("puppeteer");
...
class AllCommand extends Command {
  async run() {
    ...
    const browser = await puppeteer.launch();
    const page = await browser.newPage();
    await page.goto(
      "http://www.conmebol.com/es/copa-libertadores-2019/fixture",
      { waitUntil: "load", timeout: 0 }
    );

    // Insert some really crazy code to parse the HTML
    // you can find this at https://github.com/streaver/libertadores-cli

    this.log(results);
  }
}
```

Now we can execute `libertadores games:all` and we will get the results right there on the terminal:

[![asciicast](https://asciinema.org/a/mW5uSGbLT4ItkungqKzTkFeBd.svg)](https://asciinema.org/a/mW5uSGbLT4ItkungqKzTkFeBd)

As you may have noticed I also added a "loading" feature to give the user some visual feedback. In order to add that, you simply install the package `cli-ux` and then wrap the "slow" parts of the code in some start/stop calls:

Install it like this:

```bash
yarn add cli-ux --save
```

Add the spinner with something like:

```javascript
const { cli } = require('cli-ux');
...

cli.action.start('Fetching data');
//Do something that takes time
cli.action.stop();
...
```

Now, at this point we have the CLI, we can write tests for it! Oclif comes with some nice defaults for testing this kind of CLIs. In this particular case, you just want to test that the output to the terminal is what you expect. Fortunately, that is exactly what the auto-generated test for the command does, you only need to adapt that code!

I will leave this task to you (the reader, just like Math books) :roll_eyes:...or you can check them out in the [official repository](https://www.github.com/streaver/libertadores-cli) for the "Copa Libertadores" CLI.

Install the CLI, stay up-to-date and don't miss games anymore :heart::soccer:!