Why Build? As we already know the whole world is suffering from COVID-19 and the vaccinations are going in full swing everywhere. Finding a slot is getting tougher in our country India as we have a huge population to be vaccinated. Numerous times we have to go to to search for a slot and slots are always full. It is pretty time-consuming and irritating. Being a developer, I thought most of the time is usually spent by us in the terminal so why can't we have a basic terminal-based app to save time. So this post will help you in two ways CoWin site Learn how to create Node.js based CLI's Get real-time info about vaccination slots for your area. In case you are someone who likes to watch than read you can watch the same Let's begin our initial setup! – We are assuming you have installed Node.js and npm, If not you can install from Pre-requisite here So as a first step lets initialize our project using command npm init Enter the basic details as shown below. This will create package.json file in the folder cowinCLI. The next step is to create a bin folder that will have our index.js file containing our application. Open the index.js file and add the below-mentioned first line. This actually tells the interpreter that whatever code runs below this will be handled by the node. #! /usr/bin/env node If you remember while creating we have mentioned our entry point as index.js but actually, this file now exists in the bin folder. So we will correct that as well as we will add one more entry. The new entry which we will add is for the keyword we want to use to call our CLI. We want to use something like cowin. So we will add this entry. : { : } "bin" "cowin" "./bin/index.js" So your package.json will look something like this { : , : , : , : , : { : }, : , : , : { : , : , : , : , : , : }, : { : } } "name" "vaccli" "version" "1.0.0" "description" "CLI vaccination slots" "main" "bin/index.js" "scripts" "test" "echo \"Error: no test specified\" && exit 1" "author" "Nabheet" "license" "ISC" "dependencies" "axios" "^0.21.1" "chalk" "^4.1.1" "commander" "^7.2.0" "inquirer" "^8.0.0" "node-notifier" "^9.0.1" "tty-table" "^4.1.3" "bin" "cowin" "./bin/index.js" So the basic structure is set. Now before we start adding the functionality we have not given a thought to how we will fetch the data? Let's first check that. Do we have an API to fetch covid vaccination slots data? Thank God for looking at site they have provided us with . 50% of work is done. Now, all we need to do is, consume this data and work as per our need. Let's now think about what our CLI will do. Co-Win OpenAPI What functions our CLI will perform? On looking closely at the for a district( In India we have a Country comprised of States and Union Territories which in turn consists of districts) we can see it needs some kind of district id. calendar slots api So looking at how do we get districts id we found another but that needs state id api How do we get state id's another 😊 API So our CLI shall do the following. Ability to get all states and id's Ability to get all district id's for a state id Ability to get slots by district id Ability to filter slots by ages as we have slots by 18-45 and 45 and above. Apart from this some beautification Desktop notification In order to achieve this, we will be using multiple modules lets install them first using the below-mentioned command npm npm install axios chalk commander inquirer node-notifier tty-table Packages to be installed Axios –for calling the different API's Chalk – for beautifying the console output Commander – giving the different options and command in CLI such as cowin states or cowin districts <state id>; Inquirer – for getting user input for entering the age filter Node-notifier – send desktop notification Tty-table – format our table output Let's begin by creating separate functions for them. Create a util folder under the cowinCLI project. Create files states.js, districts.js, config.js, and slots.js in util folder. Config.js is for the configuration related common data such as table header formatting which will be used by all functions exports.config = { : { : }, }; exports.options = { : , : , : , : , : , : , : , }; // Common configuration data to be used by all functions. headers "User-Agent" "Axios - console app" borderStyle "solid" borderColor "blue" headerAlign "center" align "left" color "white" truncate "..." width "90%" Let's first code our reusable States function in states.js If you look, we need to call the states for that we will use our already installed npm package . We are calling the API and once we got a response we are formatting the table data by using the tty-table package and writing the output to the console. So this function will return the formatted output of states and its id's. API axios axios = ( ); table = ( ); { config,options } = ( ); .exports = { axios .get( , config) .then( { header = [ { : , : , : , : , : , : , }, { : , : , : , : , : , : , }, ]; out = table(header, response.data.states, options).render(); .table(out); }) .catch( { .log(error); }); }; const require 'axios' const require "tty-table" const require './config' // function to return list of all states module ( ) function "https://cdn-api.co-vin.in/api/v2/admin/location/states" ( ) => response // table formatter let value "state_id" headerColor "cyan" alias "State ID" color "white" align "left" width 40 value "state_name" alias "State" headerColor "cyan" color "white" align "left" width 40 const console ( ) => error console Let's code our second reusable Districts function in districts.js For this also we will use a similar set up of axios and tty-table. The only thing to be noted is, in this function which we are exporting has an argument as stateid. axios = ( ); table = ( ); { config,options } = ( ); .exports = { axios .get( , config ) .then( { header = [ { : , : , : , : , : , : , }, { : , : , : , : , : , : , }, ]; out = table(header, response.data.districts, options).render(); .table(out); }) .catch( { .log(error); }); }; const require 'axios' const require "tty-table" const require './config' // Function which take stateid as input and return all the formatted districts module ( ) function stateid `https://cdn-api.co-vin.in/api/v2/admin/location/districts/ ` ${stateid} ( ) => response // Table header specific formatting let value "district_id" headerColor "cyan" alias "District ID" color "white" align "left" width 40 value "district_name" alias "District" headerColor "cyan" color "white" align "left" width 40 // Output the results. const console ( ) => error console Let's code our third reusable slots function in slots.js For this also we will use a similar set up of axios and tty-table. The only thing to be noted is in this function which we are exporting has an argument as districtid. In addition to it you can see we are using chalk and inquirer package. Chalk is used to format the headers above the table and inquirer is used for taking input from user when slots command is run. We have also used node-notifier which will send desktop notification as soon as it runs, just an example. You can modify this behavior to code your own custom logic. axios = ( ); table = ( ); chalk = ( ); notifier = ( ); inquirer = ( ); { config,options } = ( ); .exports = { inquirer .prompt([ { : , : , : , : [ { : , : , }, { : , : , }, { : , : , }, ], }, ]) .then( { date = (); todaysDate = ; .log( chalk.underline.bgRed.bold( ) ); axios .get( , config ) .then( { finalData = []; districtName; response.data.centers.forEach( { item.sessions.forEach( { districtName = item.district_name; (answers.choice == ) { data = { : item.name, : item.address, : session.date, : session.available_capacity, : session.min_age_limit, }; finalData.push(data); } ( answers.choice == && session.min_age_limit == ) { data = { : item.name, : item.address, : session.date, : session.available_capacity, : session.min_age_limit, }; finalData.push(data); } ( answers.choice == && session.min_age_limit == ) { data = { : item.name, : item.address, : session.date, : session.available_capacity, : session.min_age_limit, }; finalData.push(data); } }); }); .log( chalk.underline.bgGreen.bold( ) ); (answers.choice) { : .log(chalk.underline.bgBlue.bold( )); ; : .log(chalk.underline.bgBlue.bold( )); ; : .log(chalk.underline.bgBlue.bold( )); ; : ; } header = [ { : , : , : , : , : , }, { : , : , : , : , : , }, { : , : , : , : , : , }, { : , : , : , : , : , }, { : , : , : , : , : , }, ]; out = table(header, finalData, options).render(); .table(out); notifier.notify({ : , : , : , : , }); }) .catch( { .log(error); }); }) .catch( { (error.isTtyError) { } { } }); }; const require 'axios' const require "tty-table" const require "chalk" const require "node-notifier" var require "inquirer" const require './config' // function to check slots. module ( ) function district //Input prompt for getting what age you want to check records. type "list" name "choice" message "Which Age group?" choices name "View All" value "" name "45 Plus" value "45" name "18 - 45 " value "18" ( ) => answers const new Date var ` - - ` ${date.getDate()} ${ ( date.getMonth() + ).padStart( , )} String 1 2 "0" ${date.getFullYear()} console `Showing Slots from - ` ${todaysDate} `https://cdn-api.co-vin.in/api/v2/appointment/sessions/public/calendarByDistrict?district_id= &date= ` ${district} ${todaysDate} ( ) => response let let ( ) => item ( ) => session // based on user age choice filter the data if "" let Center Address Date FreeSlots Age else if "18" "18" let Center Address Date FreeSlots Age else if "45" "45" let Center Address Date FreeSlots Age console `District - ` ${districtName} switch case "" console `All ages` break case "45" console `45+ Age` break case "18" console `18-45 Age` break default break // table formatting let value "Center" headerColor "cyan" color "white" align "left" width 40 value "Address" headerColor "cyan" color "white" align "left" width 40 value "Date" headerColor "cyan" color "white" align "left" width 15 value "FreeSlots" headerColor "cyan" color "white" align "left" width 20 value "Age" headerColor "cyan" color "white" align "left" width 20 const console title "Vaccination Slots Available" subtitle "Daily Maintenance" message "Immediately go and check Vaccination slots!" wait true ( ) => error console ( ) => error if // Prompt couldn't be rendered in the current environment else // Something else went wrong Now our all basic functions are in place but what is pending is the actual CLI😊 Let's start building that. Lets now build the CLI by updating index.js So far we have used all npm packages except commander it is the heart of our CLI. We will be using commander for making the subcommands as well as flag options. As can be seen, below we have used both command and option. Commands for getting states, districts, and slots and they have a callback function mentioned as our reusable functions under action. program = ( ); districts = ( ); states = ( ); slots = ( ); program.option( , ); program .command( ) .description( ) .action(states); program .command( ) .description( ) .action(districts); program .command( ) .description( ) .action(slots); program.parse(); #! /usr/bin/env node const require "commander" // import all functions const require '../util/districts' const require '../util/states' const require '../util/slots' // adding different cli options,commands and passing callback functions in actions "-a, --available" "Output If slots available" "states" "Get all State Codes and descriptions." "district <stateid>" "Get all district of a State" "slots <districtid>" "Get slots for the district" Final touches So we have everything ready all we need to do is now run the below command which will install our package globally. npm install -g . Output cowin states cowin districts 12 cowin slots 187 You can refer to the source code Previously published at https://blogs.sap.com/2021/05/15/lets-build-node.js-based-cli-track-real-time-covid-19-vaccination-slots-in-india/?update=updated