How to Create Customs Command Line using Node.js
Completing long tedious tasks by the call of a script using Node.js in the Command-Line.
When you find yourself repeating a certain task over and over again chances are you can automate those tasks with a script, making the rate of completing your task faster. The Command Line Interface(CLI) is a great tool for automating, It allows running certain tasks right from the terminal.
Programmers, advanced computer users or administrators use the CLI in their everyday life. There are certain situations where typing into the CLI would produce faster results than simply using the Graphical User Interface (GUI). CLI connects a user to a computer program or operating system; with it, users can interact with a system or applications by typing in text-based commands.
CLI scripts can be built using different programming languages such as Python, Java, Go or Nodejs, inter alia.
In this article, We are going to learn How to Create a CLI tool using Node js.
Prerequisites
- Basic knowledge of JavaScript
- A recent version of Node and npm installed.
- Terminal (CMD or any other terminal of your choice)
- Code editor (Visual Studio Code or any other one you prefer)
When creating a CLI in Node.js we can tap into its rich npm ecosystem by using already available dependencies which are CLI focused. Here is a list of the dependencies used.
commander
: complex parsing of arguments and command help systemchalk
: for displaying coloured textfiglet
: for creating a fancy ASCII-based banneraxios
: for retrieving and sending data to an API.
Getting Started
For this tutorial, we are creating a new Node.js project from scratch. Change your directory to the folder where you want the project to live.
Start by opening a terminal in that file directory, enter the following code to create a project folder, navigate into it and initialize the project.
mkdir hello-world
cd hello-world
npm init -y
Next, open the newly created project in your preferred code editor, create a new file named index.js
open the index.js
file and insert this code:
#!/usr/bin/env node
console.log("Hello-World");
Getting us introduced to writing script commands for CLI: We are printing a simple Hello-World, and the first line of code here is called a shebang line.
Since our script is executed via the command line (e.g., via a bash shell or zsh shell). The shebang line directs the command line to the interpreter, it is to pass our script. Here we want our code to be interpreted by Node.js.
When we run the script file through the command line. Our code is passed to Node.js for interpretation. Hence if this line is excluded then we should expect our code to throw an error as the compiler would not understand the code.
Next, open the package.json
file we generated from initializing our project previously. Insert a new key: bin
. When we are ready to install our project globally, npm process the package.json file of the module and search for the bin
field in it.
bin
field is an object, inside it is the terminal command hello-world
and the value is the .js
file which is to be executed when the user calls the terminal command.
Also, we are adding another key value to the package.json file
A type
key with the value module
;
By default .js files in Node.js are considered CommonJS modules. To make .js files execute as ES modules, Here we simply set type
field as module
in the package.json: this allows the reuse of code and dependencies using export and import statement in the project.
"type": "module",
"bin": {
"hello-world": "./index.js"
}
At this point, our package.json file should look like this:
{
"name": "hello-world",
"version": "1.0.0",
"description": "",
"main": "index.js",
"type": "module",
"bin": {
"hello-world": "./index.js"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
We can run the script by calling the .js file just like any Node.js application. Enter this in the command line.
node index.js
However, the goal of creating a CLI is to be able to call it from anywhere in the system. We can do that by installing it globally with npm install command
.
npm install -g .
💡 Tip: You can list all installed global modules in Node.js using
npm ls -g --depth=0
The above code, installs the CLI globally, Any command specified in our bin object in the package.json file will now act as the Terminal command to execute the script.
As a result, we can execute the script by typing hello-world
in the terminal.
Custom text with colors and borders
Texts are displayed plainly in the CLI, but if we want a better and custom UI. For example, you may want to display colors or add borders to text.
The chalk
and figlet
modules are one of the many dependencies we can use to beautify our CLI output. Add both dependencies to the project:
npm install chalk figlet
Next step, replace the content of index.js
with:
#!/usr/bin/env node
import chalk from "chalk";
import figlet from "figlet";
import boxen from "boxen";
console.log(
chalk.magentaBright(
figlet.textSync("Beautifying CLI", { horizontalLayout: "full" })
)
);
const text = chalk.white.bold("Hello-World!");
const options = {
padding: 1,
margin: 1,
borderStyle: "round",
borderColor: "green",
backgroundColor: "#555555",
};
console.log(boxen(text, options));
Let us run the project terminal command hello-world
and see the result
Passing arguments in CLI
CLI applications can accept one or more command line arguments, such as optional or required parameters, and other configurations depending on our logic.
Although we can get the command line argument from the Node.js inbuilt process.argv values. But there are modules that we can use, that save time and effort and also provide a --help feature. The commander
dependency is one of those modules designed to support the common CLI task.
First, install the commander
as a dependency for our application:
npm install commander
Next, update the index.js
file with the code below:
#! /usr/bin/env node
import chalk from "chalk";
import figlet from "figlet";
import { Command } from "commander";
const program = new Command();
console.log(
chalk.magentaBright(
figlet.textSync("HelloWorld App:", { horizontalLayout: "full" })
)
);
program
.command("login")
.argument("<username>", "user to login")
.argument("[password]", "password for user, if required", "no password")
.action(auth);
function auth(username, password) {
if (!username || password == "no password")
console.log(chalk.redBright.bold("Login Falied"));
if (username && password !== "no password")
console.log(chalk.greenBright.bold("Login Success"));
console.log("username:", username);
console.log("password:", password);
}
program.parse();
The above code imports the commander module and configures it to require two types of arguments. The arguments in a commander may be < required > or [optional].
In the code, we created a Hello World App using login argument values gotten from the commander module to authenticate that a username and password are passed in our terminal.
Next, save the script and execute the command hello-world
in the terminal.
After running the command script you would notice the commander module added a --help
feature to our CLI.
💡 Tip: To learn more use case of the commander module check here
Fetching APIs from the Command Line
Some common task we carry out as developers requires us to call APIs to get data or send data to an API endpoint. We are going to be fetching a random excuse API and displaying it in the CLI console. By making use of the Axios npm module.
Axios is a popular JavaScript module. It is used to perform HTTP requests, which work in Browsers and Node.js platforms.
Start by installing Axios to the project:
npm install axios@0.21.1
Next, replace the code in the index.js
file with the below code
import chalk from "chalk";
import figlet from "figlet";
import axios from "axios";
import { Command } from "commander";
const program = new Command();
console.log(
chalk.magentaBright(
figlet.textSync("Hello Excuse Api Fetch:", { horizontalLayout: "full" })
)
);
program
.command("fetch")
.alias("--f")
.argument("<username>", "provide username")
.action(auth);
function auth(username) {
console.log(`Hello ${username} Welcome`);
const authOptions = {
method: "GET",
url: "https://excuser.herokuapp.com/v1/excuse",
};
axios
.request(authOptions )
.then(function (response) {
console.log(
"I'm late because: " + chalk.green.bold(response.data[0].excuse)
);
console.log(
"Excuse Category:" + chalk.green.bold(response.data[0].category)
);
})
.catch(function (error) {
console.error(error);
});
}
program.parse();
Let’s take a peep at what is going on in our code snippet above:
- We import all the dependencies we need to make the API request, read the argument typed into the terminal and beautify our output display. Then instantiate the commander dependency and assign it to a
program
variable. - We logged a hello message using chalk and figlet dependencies.
- We specify (sub)commands using .command(), Here in the command() you specify the command name. Then specify the command arguments separately using .arguments(). which can be or [optional]. Lastly we call an action() handler. The .command() is implemented using the action() handler. Calling the auth function
- auth function is defined here and it accepts a parameter
username
from the command argument. Also makes an axios request passing relevant config method and URL. - Finally, program.parse() is called, This method, parses the arguments passed into our script. The parameter is optional and defaults to process.argv, which is an array containing the command line arguments. From here the commander takes all arguments passed to the script and are passed to the action handler.
Next, call the hello-world
command and make a fetch request to the API.
Conclusion
Wrapping up, we learned how to build a command-line application with Node.js. We learned how to set up a Node.js CLI project, also how to enable JavaScript modules by modifying the package.json file to include a "type": "module" field, as well as a bin field to specify a CLI command word on our computer after our package is installed.
Using shebang line to specify to the console which interpreter to use for reading our code and how to use various CLI focused dependencies.