How to create a CLI with Node.js
Matheus Tanaka
Posted on May 17, 2022
A command-line interface is a simple way to execute commands, mostly used by Operational Systems, like Linux, macOS, and Windows. But, it can be used to install packages and run scripts like git or npm.
Node.js is a great runtime to build CLI where you can run it on any machine that has Node installed.
By the way, this article is for anyone who wants to build your first CLI and automate little things.
Getting Started
First of all, you need to install Node.js runtime on your machine. If you don’t have Node.js installed, it can’t work. So, go to https://nodejs.org/en/ and download it.
We will use two packages to build the CLI:
- node-fetch - is a fetch client that we can use to get the data from any API
- yargs - will allow us to process any flags or arguments passed to the CLI
To install the packages, just type in your terminal:
npm install node-fetch yargs --save
The API
There are so many public APIs, you can choose any API. I want to make a CLI that monitors cryptocurrency price, so I choose an API that Binance provide for us.
Coding
You should create a file called crypto.mjs, you can name it as you want, I put crypto because my CLI is about crypto, but feel free to rename it if you want. I recommend index.mjs or main.mjs, but whatever.
First, we will import our packages and define our interpreter, as you can see, we are using node.
#! /usr/bin/env node
import fetch from "node-fetch";
import yargs from "yargs";
With everything imported, it’s time to code, let’s do that.
#! /usr/bin/env node
import fetch from "node-fetch";
import yargs from "yargs";
//The process.argv property returns an array containing the command-line arguments passed when the Node.js process was launched
const { argv } = yargs(process.argv);
//init fetch to binance api
const response = await fetch("<https://api2.binance.com/api/v3/ticker/24hr>");
const data = await response.json();
console.log(data);
After fetching the data, we need to see if our response works correctly, I recommend putting a console.log(data) and checking the API response.
In Binance API there are so many cryptocurrencies listed, when you fetch them, will return so many cryptos.
Your response should be like that:
As you can see, it works. It’s impossible to list everything in a unique image, if you want to check all of these cryptos, you can install an API platform like Postman or Insomnia.
What’s the purpose of my CLI?
I defined three functions that CLI should do, then we will translate them into code.
- Return The largest Crypto Coin Price in the last 24 Hours
- Return Bitcoin Average Price in the last 24 Hours
- Return Ethereum Average Price in the last 24 Hours
//The first function to return the largest crypto
//Defining the properties that we want to show with CLI
let largestPrice = "";
let symbol = "";
let priceChangePercent = "";
let highPrice = "";
let dollarUSLocale = Intl.NumberFormat("en-US"); // convert the value in dollar
//for each crypto, we should check the last price and return the largest
data.forEach((crypto) => {
if (crypto.lastPrice > largestPrice) {
largestPrice = crypto.lastPrice;
symbol = crypto.symbol;
priceChangePercent = crypto.priceChangePercent;
highPrice = crypto.highPrice;
}
});
Maybe you are asking yourself why there are so many variables?
The answer is the API has so many properties, like lastPrice, symbol and others, so we need to define which properties we want to cover with CLI, I choose these with empty strings, but you can put more properties, just check the API response and create a variable to store the data.
How we will return the largest crypto?
data.forEach((crypto) => {
if (crypto.lastPrice > largestPrice) {
largestPrice = crypto.lastPrice;
symbol = crypto.symbol;
priceChangePercent = crypto.priceChangePercent;
highPrice = crypto.highPrice;
}
});
Look at the API Response
{
symbol: 'BNBUSDT',
priceChange: '14.00000000',
priceChangePercent: '4.747',
weightedAvgPrice: '301.42344940',
prevClosePrice: '295.00000000',
lastPrice: '308.90000000',
lastQty: '4.30600000',
bidPrice: '308.90000000',
bidQty: '93.80300000',
askPrice: '309.00000000',
askQty: '214.39300000',
openPrice: '294.90000000',
highPrice: '310.70000000',
lowPrice: '292.10000000',
volume: '697249.05200000',
quoteVolume: '210167214.34190000',
openTime: 1652709545294,
closeTime: 1652795945294,
firstId: 551442887,
lastId: 551762252,
count: 319366
},
{
symbol: 'VENBNB',
priceChange: '0.00000000',
priceChangePercent: '0.000',
weightedAvgPrice: '0.00000000',
prevClosePrice: '0.14920000',
lastPrice: '0.00000000',
lastQty: '0.00000000',
bidPrice: '0.00000000',
bidQty: '0.00000000',
askPrice: '0.00000000',
askQty: '0.00000000',
openPrice: '0.00000000',
highPrice: '0.00000000',
lowPrice: '0.00000000',
volume: '0.00000000',
quoteVolume: '0.00000000',
openTime: 1652369888551,
closeTime: 1652456288551,
firstId: -1,
lastId: -1,
count: 0
},
As you can see, we will loop through the API data, so here, in the response, I got two cryptos BNBUSDT and VENBNB. Our forEach will check if the property of BNBUSDT lastPrice is bigger than VENBNB lastPrice, if it’s true, largestPrice will store the lastPrice of BNB and check if the next crypto is largest than the last again.
With the logic done, it’s time to create our first command, go to package.json and type this below the script
"scripts": {
"test": "echo \\"Error: no test specified\\" && exit 1"
},
"bin": {
"crypto-status": "./crypto.mjs"
},
The bin folder holds binary files, which are the actual executable code for your application or library.
With bin added, we will pass the argv and the flag, so come back to crypto.mjs file and type the rest of code, as you can see is argv.price is the command
// Commands CLI: crypto-status --price
if (argv.price) {
console.log(`
The largest Crypto Coin Price in the last 24 Hours\\n
Price: $${dollarUSLocale.format(largestPrice)}\\n
Symbol: ${symbol}\\n
Price Change Percent: %${priceChangePercent}\\n
High Price: $${dollarUSLocale.format(highPrice)}
`);
}
- Return The largest Crypto Coin Price in the last 24 Hours is done✅
Now we need to make the last two functions, they don’t have to verify if the price is bigger than the other, I just want to check the price of them, so I decided to put them inside of commands and filter the price in the last 24 hours.
I will pass the argument and the flag, then we can filter these properties that we want to execute with CLI.
// Commands CLI: crypto-status --ethereum
if (argv.ethereum) {
data.filter((crypto) => {
if(crypto.symbol == "ETHUSDT") {
console.log(`
Ethereum Avarage Price in the last 24 Hours\\n
Symbol: ${crypto.symbol}\\n
Price: $${dollarUSLocale.format(crypto.lastPrice)}\\n
Price Change Percent: %${crypto.priceChangePercent}\\n
High Price: $${dollarUSLocale.format(crypto.highPrice)}`)
}
})
}
// Commands CLI: crypto-status --bitcoin
if (argv.bitcoin) {
data.filter((crypto) => {
if(crypto.symbol == "BTCUSDT") {
console.log(`
Bitcoin Avarage Price in the last 24 Hours\\n
Symbol: ${crypto.symbol}\\n
Price: $${dollarUSLocale.format(crypto.lastPrice)}\\n
Price Change Percent: %${crypto.priceChangePercent}\\n
High Price: $${dollarUSLocale.format(crypto.highPrice)}`)
}
})
}
- Return Bitcoin Average Price in the last 24 Hours is done✅
- Return Ethereum Average Price in the last 24 Hours is done✅
Final Code
#! /usr/bin/env node
import fetch from "node-fetch";
import yargs from "yargs";
const { argv } = yargs(process.argv);
const response = await fetch("<https://api2.binance.com/api/v3/ticker/24hr>");
const data = await response.json();
let largestPrice = "";
let symbol = "";
let priceChangePercent = "";
let highPrice = "";
let dollarUSLocale = Intl.NumberFormat("en-US");
data.forEach((crypto) => {
if (crypto.lastPrice > largestPrice) {
largestPrice = crypto.lastPrice;
symbol = crypto.symbol;
priceChangePercent = crypto.priceChangePercent;
highPrice = crypto.highPrice;
}
});
// Commands CLI: crypto --price
if (argv.price) {
console.log(`
The largest Crypto Coin Price in the last 24 Hours\\n
Price: $${dollarUSLocale.format(largestPrice)}\\n
Symbol: ${symbol}\\n
Price Change Percent: %${priceChangePercent}\\n
High Price: $${dollarUSLocale.format(highPrice)}
`);
}
// Commands CLI: crypto-status --ethereum
if (argv.ethereum) {
data.filter((crypto) => {
if(crypto.symbol == "ETHUSDT") {
console.log(`
Ethereum Avarage Price in the last 24 Hours\\n
Symbol: ${crypto.symbol}\\n
Price: $${dollarUSLocale.format(crypto.lastPrice)}\\n
Price Change Percent: %${crypto.priceChangePercent}\\n
High Price: $${dollarUSLocale.format(crypto.highPrice)}`)
}
})
}
// Commands CLI: crypto-status --bitcoin
if (argv.bitcoin) {
data.filter((crypto) => {
if(crypto.symbol == "BTCUSDT") {
console.log(`
Bitcoin Avarage Price in the last 24 Hours\\n
Symbol: ${crypto.symbol}\\n
Price: $${dollarUSLocale.format(crypto.lastPrice)}\\n
Price Change Percent: %${crypto.priceChangePercent}\\n
High Price: $${dollarUSLocale.format(crypto.highPrice)}`)
}
})
}
Your code should look like the code above.
Now you can test these commands in your terminal to check if everything is working.
$ node crypto.mjs crypto-status --price
$ node crypto.mjs crypto-status --bitcoin
$ node crypto.mjs crypto-status --ethereum
How to install our package locally?
Lastly, we must install our package locally so we can test out the CLI. We could just execute the file with the node runtime, but we want to see the CLI work.
bash npm install -g
We can simply install with no args which tells npm to install the current director. The -g flag means we want to globally install this package vs in a local node_modules.
You should now be able to run and see your log print.
bash crypto-status
Hopefully, the tutorial above helped you to learn more about Node.js and CLI. If you have any doubt, feel free to leave comments about them.
If you learned something from this article, please hit the like button.
- If you want to talk with me, please send me a message on Twitter.
- Source Code.
- crypto-status-cli
Posted on May 17, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.