Build Your Own CLI Word Counter in Node.js

I believe the best way to master a concept is to explain it to someone else. I’m a Full Stack Developer navigating the ecosystems of JavaScript and Python, building everything from responsive frontends to robust backends. I use this space to document my learning journey, break down complex topics, and share practical solutions to the bugs I encounter. Let's learn in public together!
Have you ever wanted to build your own Command Line Interface (CLI) tool?
In this Blog, I’ll create a simple CLI app in Node.js that counts the number of words in a file. Along the way, we’ll explore how Node.js handles command-line arguments with process.argv and how to make our CLI professional using the commander library.
Step 1: A Simple Word Counter
Let’s begin with a very basic Node.js program that reads a file and counts words:
const fs = require("fs");
function countWords(fileName) {
fs.readFile(fileName, "utf-8", function (err, data) {
let count = 0;
for (let i = 0; i < data.length; i++) {
if (data[i] == " ") {
count++;
}
}
console.log(count + 1);
});
}
countWords("a.txt");
This works. If a.txt has text, the script will count its words.
But here’s the problem: this isn’t really a CLI. We can’t do things like node index.js -help, and it only works with a hardcoded file.
Step 2: Making It Dynamic with process.argv
In Node.js, anything passed after the script name in the command line can be accessed using process.argv.
console.log(process.argv);
//In console, if you write "node index.js filePath", then the console output will be
[
'C:\\Program Files\\nodejs\\node.exe',
'C:\\Users\\HarshGupta\\Desktop\\index.js',
'C:\\Users\\HarshGupta\\Desktop\\a.txt'
]
Here:
process.argv[0]→ path to Node.jsprocess.argv[1]→ path to our scriptprocess.argv[2]→ the file path we passed
So, we can make our script dynamic like this:
const fs = require("fs");
function countWords(fileName) {
fs.readFile(fileName, "utf-8", function (err, data) {
let count = 0;
for (let i = 0; i < data.length; i++) {
if (data[i] == " ") {
count++;
}
}
console.log(count + 1);
});
}
countWords(process.argv[2]);
Now we can pass any file path dynamically, but still it doesnt feel like a CLI.
Step 3: Turning It Into a Proper CLI with Commander
To add features like --help, --version, and subcommands, we’ll use the commander library.
First install it:
npm install commander
Now, update our code:
const fs = require("fs");
const { Command } = require("commander");
const program = new Command();
program
.name("Counter")
.description("Counts the number of words in a file")
.version("0.0.0");
program
.command("count")
.description("Counts the number of words in a file")
.argument("<file>", "file to count")
.action((fileName) => {
fs.readFile(fileName, "utf-8", (err, data) => {
if (err) {
console.log(err);
} else {
let count = 0;
for (let i = 0; i < data.length; i++) {
if (data[i] == " ") {
count++;
}
}
console.log(`There are ${count + 1} words in ${fileName}`);
}
});
});
program.parse();
Step 4: Running the CLI
Now let’s try it out.
Run:
node index.js count C:\Users\HarshGupta\Desktop\a.txt
Outputs:
There are 5 words in C:\Users\HarshGupta\Desktop\a.txt
We also get built-in CLI support for free:
node index.js --help
Usage: Counter [options] [command]
Counts the number of words in a file
Options:
-V, --version output the version number
-h, --help display help for command
Commands:
count <file> Counts the number of words in a file
help [command] display help for command
More to it
You can add as many command you want, like this:
const fs = require("fs");
const {Command} = require("commander");
const program = new Command;
program
.name("Counter")
.description("Counts the number of words and lines in a file")
.version("0.0.0");
program.command("count")
.description("Counts the number of words in a file")
.argument('<file>', 'file to count')
.action((fileName) => {
fs.readFile(fileName, "utf-8", (err, data) => {
if(err){
console.log(err);
}
else{
let count = 0;
for(let i = 0; i < data.length; i++){
if(data[i] == " "){
count++;
}
}
console.log(`There are ${count + 1} words in ${fileName}`);
}
})
})
program.command("count_lines")
.description("Counts the number of lines in a file")
.argument('<file>', 'file to count')
.action((fileName) => {
fs.readFile(fileName, "utf-8", (err, data) => {
if(err){
console.log(err);
}
else{
let count = 0;
for(let i = 0; i < data.length; i++){
if(data[i] == "\n"){
count++;
}
}
console.log(`There are ${count} lines in ${fileName}`);
}
})
})
program.parse();
Conclusion
We just built a fully functional CLI word counter from scratch using Node.js. Starting with a simple script, we made it dynamic using process.argv and then transformed it into a professional-grade CLI using commander.
This project might seem small, but the skills you’ve learned here are foundational for building real-world developer tools. Most CLI tools you use daily — like npm, git, or eslint — are built on the same principles.



