Max Schmitt

June 7 2020

Deeply nested subcommands in Node CLIs with Commander.js

For a recent project I was leveraging the awesome Commander.js framework to build out a CLI with a ton of commands and subcommands.

Example: Fast-food CLI

Let's say you're building a CLI to order food from a fast food restaurant with the following interface:

Program overview:
$ mcd
Order some food:
$ mcd order
Order a burger menu:
$ mcd order burger
Order some dessert:
$ mcd order dessert
Pay a meal:
$ mcd pay

File structure

With Commander.js, you can easily implement above interface using the following file structure:

cli/
mcd
mcd-order
mcd-order-burger
mcd-order-dessert
mcd-pay

😍 Note how neatly self-documenting this file structure is.

Each of these files is a standalone executable and could be used on its own (don't forget to run chmod +x cli/*).

Code

The most top-level program (mcd) will declare its subcommand order.

cli/mcd

#!/usr/bin/env node
const { program } = require('commander')
// mcd-order
program.command('order', 'Order some food')
program.action(() => {
program.help()
})
program.parse(process.argv)

Important: program.command must be invoked with the second argument (description), as this will hint to Commander that the subcommand lives in its own executable.

The subcommand mcd-order will declare its own subcommands using the same syntax:

cli/mcd-order

#!/usr/bin/env node
const { program } = require('commander')
program
// mcd-order-burger
.command('burger', 'Order a burger menu')
// mcd-order-dessert
.command('dessert', 'Order some dessert')
program.action(() => {
program.help()
})
program.parse(process.argv)

And finally, at the end of the chain, mcd-order-burger does not declare any more subcommands:

cli/mcd-order-burger

#!/usr/bin/env node
const { program } = require('commander')
program.action(() => {
console.log('Thank you for ordering some burgers!')
})
program.parse(process.argv)

And that's how you can deeply nest subcommands for your Node.js CLI using Commander.js!

For a complete reproducible example, check out the repository on GitHub.