Building CLI Tools (commander)
A CLI tool is a Node program you run from the terminal that reads arguments, options, and flags to do a job — and commander is the popular library that parses those inputs and generates help text for you.
Learn Building CLI Tools (commander) in our free Node.js course — a beginner-friendly interactive lesson with worked examples, a practice exercise and a…
Part of the free Node.js course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
In this lesson you'll read raw arguments with process.argv , build a parser by hand to understand the mechanics, then use commander for commands, options, and flags, and finally make your tool installable with a shebang and a bin entry.
What You'll Learn in This Lesson
1️⃣ process.argv: the Raw Arguments
When you run node greet.js --name Ada , Node fills process.argv with the executable path, the script path, then your arguments. So you start with process.argv.slice(2) to get just the user's input. The hand-written parser below turns --name Ada --excited into a tidy options object — value-bearing options become strings, lone flags become true .
2️⃣ commander: Commands, Options & Flags
Hand-parsing gets old fast. commander lets you declare your CLI: a name, version, commands (with required <arguments> ), options that take a value ( --count <n> , with a default), and boolean flags ( --excited ). It parses everything, validates it, and auto-generates --help and --version .
3️⃣ Making It an Installable Command
To let users type greet instead of node index.js , add a shebang ( #!/usr/bin/env node ) as the first line, then map a command name to your file in package.json's bin field. Run npm link to test it locally. For interactive prompts, libraries like inquirer ask the user questions and read their answers.
Your turn. The parser below works once you fill in the single blank marked ___ . Follow the 👉 hint, then run it.
Mini-Challenge: A Command Router
No blanks — just a brief and an outline. Build a git-style router where the first word is the command and the rest are flags, then act on the add command. Build it, run it, and compare with the example output.
Common Errors (and the fix)
- ❌ Forgetting slice(2) — your first "argument" is the node path. ✅ Always start from process.argv.slice(2) .
- ❌ Command not found after install — missing shebang or bin entry, or the file isn't executable. ✅ Add #!/usr/bin/env node , set bin in package.json, and npm link .
- ❌ Numeric options treated as strings — argv values are always strings. ✅ Convert with Number(opts.count) before doing math.
- ❌ Crash on missing required argument — hand-rolled parsers don't validate. ✅ Let commander mark arguments required and print usage automatically.
- ❌ "env: node: No such file or directory" — Node isn't on PATH, or wrong shebang. ✅ Use #!/usr/bin/env node and ensure Node is installed/on PATH.
Pro Tips
- 💡 Reach for commander early: hand-parsing is great for learning, but a library saves you from edge cases and gives free help text.
- 💡 Use npm link while developing: it puts your command on PATH so you can test it like a real install.
- 💡 Add prompts only when needed: flags are scriptable; interactive inquirer prompts are friendlier for humans but harder to automate.
📋 Quick Reference — CLI Building Blocks
Task
Code
Get your args
process.argv.slice(2)
Define a command
program.command('hello <name>')
Option with value
.option('-c, --count <n>')
Boolean flag
.option('--excited')
Make executable
#!/usr/bin/env node
Name the command
"bin": { "greet": "./index.js" }
Test locally
npm link
Frequently Asked Questions
🎉 Lesson Complete!
- ✅ process.argv holds raw args; slice(2) gives you the user's input
- ✅ You can hand-parse options and flags, but it gets tedious fast
- ✅ commander declares commands, options, and flags and generates help
- ✅ A #!/usr/bin/env node shebang makes a file runnable as an executable
- ✅ A package.json bin entry maps a command name to your file
- ✅ npm link tests it locally; inquirer adds interactive prompts
- ✅ Next lesson: Publishing npm Packages — share your tool with the world
Practice quiz
What does process.argv contain?
- Only environment variables
- The exit code
- The command-line arguments the process started with
- The current directory
Answer: The command-line arguments the process started with. process.argv is an array of command-line arguments passed to the process.
What are the first two elements of process.argv?
- The node executable path and the script path
- The first two user arguments
- PORT and HOST
- Always undefined
Answer: The node executable path and the script path. argv[0] is the node binary path and argv[1] is the script path; your arguments follow.
Why do CLI scripts usually call process.argv.slice(2)?
- To remove the last two arguments
- To convert arguments to numbers
- To sort the arguments
- To drop the node and script paths and keep only user arguments
Answer: To drop the node and script paths and keep only user arguments. slice(2) drops the first two entries (node + script path), leaving just the user's arguments.
Why use commander instead of hand-parsing argv?
- It runs scripts faster
- It handles options, flags, defaults, subcommands, and auto help
- It is required by Node
- It encrypts arguments
Answer: It handles options, flags, defaults, subcommands, and auto help. commander parses options, validates input, supports subcommands, and auto-generates --help and --version.
What does the #!/usr/bin/env node shebang line do?
- Tells the OS to run the file with node found on PATH
- Imports the node module
- Sets an environment variable
- Comments out the first line
Answer: Tells the OS to run the file with node found on PATH. The shebang tells the OS which interpreter to use; env node finds node on the user's PATH portably.
Where must the shebang line appear in a CLI file?
- On the last line
- Anywhere in the file
- On the very first line
- Inside the main function
Answer: On the very first line. The shebang only works as the very first line of the script.
Which package.json field maps a command name to your script file?
- main
- bin
- scripts
- exports
Answer: bin. The bin field, like "bin": { "greet": "./index.js" }, maps a command name to the entry file.
What does npm link do during development?
- Publishes the package to npm
- Installs all dependencies
- Runs the test suite
- Symlinks your command globally so you can run it by name
Answer: Symlinks your command globally so you can run it by name. npm link symlinks the bin command onto your PATH so you can test it like an installed tool.
In the hand-written parser, how is a token like '--name' detected as an option?
- It contains a space
- It starts with '--'
- It is all uppercase
- It is the first argument
Answer: It starts with '--'. The parser checks arg.startsWith('--') to treat a token as an option key.
Which library is mentioned for asking the user interactive questions?
- commander only
- express
- inquirer / @inquirer/prompts
- dotenv
Answer: inquirer / @inquirer/prompts. For interactive prompts, libraries like inquirer or @inquirer/prompts read answers from the user.
Continue this course
- Previous: Security Best Practices
- Next: Publishing npm Packages