Command-line utilities are the most basic and beautiful apps ever created, the apps that started it all. We use command-line utilities every day, whether it be git, grep, awk, npm, or any other terminal app. CLIs are super useful and usually the fastest way to get something done. Do you have something in your specific workflow that you have to do over and over again? Chances are that can be automated with a CLI. We are going to use to make our CLI if it wasn’t clear from the title itself. Why? Because the Node.js ecosystem has thousands of extremely useful packages that we can utilize to achieve what we are trying to do. Whatever it may be that you are trying to do, it is highly probable that there exists a package for it on also node has built-in libraries to do a lot of things like Apart from that CLIs built in Node.js are , meaning they are easy to install on different OSs. Node.js npm, handling files, launching other applications, asserting tests, etc. highly portable For the purpose of this tutorial, we’ll be building a simple CLI to . We’ll accept string type arguments, parse them into a sentence, shoot them off to a translation API which will fetch us the translations, and then display the result. The complete code for this can be found on the . Let’s dive right into it! translate between languages Github repository Prerequisites Here are the tools that are required to make the CLI so make sure you have them before starting: A recent version of installed. Node.js A text editor. and that’s probably it. Setting up the project We’ll be setting up a basic Node.js project: 1. Open up your terminal. 2. Create a folder for the project. ~$mkdir termTranslate 3. Navigate to it. ~$cd termTranslate 4. Initialize a Node.js project in that folder. ~$npm init 5. Fill in the prompt. Your project is now set up. Building the basic CLI Now that we have our node project ready we move to actually making the CLI. Here’s what you have to do: 1. Create a folder named in the root directory of your project. bin 2. Inside create a file called This is going to be the entry point of our CLI. bin index.js 3. Now open the file and change the “main” part to . package.json bin/index.js 4. Now manually add another entry into the file called bin and set it’s key to and it’s value to . The addition should look something like this: package.json tran ./bin/index.js : { : } "bin" "tran" "./bin/index.js" The key, , is the . This is the keyword that people will type in the terminal for using your CLI. Be free to name it whatever you like, although I would suggest keeping the name short and semantic so that it’s quick to type and easy to remember. tran keyword for calling the CLI The name defined here is and can be changed whenever you like. not permanent Your entire file should look something like this: package.json { : , : , : , : , : { : }, : [ ], : { : }, : , : } "name" "termtran" "version" "1.0.0" "description" "A CLI to translate between languages in the terminal" "main" "bin/index.js" "scripts" "test" "echo \"Error: no test specified\" && exit 1" "keywords" "cli" "bin" "tran" "./bin/index.js" "author" "Your Name" "license" "ISC" Don’t forget to add the extra comma after adding the new entry into the file. That is an easy mistake. Note: package.json 5. Open the index.js file in the bin folder. And put the following code in it: .log( ); #! /usr/bin/env node console "Hello World!" The first line starting with is called a line or a line. A shebang line is used to specify the absolute path to the interpreter that will run the below code. The shebang line used here is for Linux or UNIX type systems but node requires it for Windows and macOS too, for proper installation and execution of the script. #! shebang bang Now let’s install and test our CLI. People may call our CLI from anywhere in the system so let's install it globally. 6. Navigate to the root directory of the project and then run ~$npm install -g . The flag tells npm to install the package globally on the system. -g Test the CLI by typing the specified keyword in the terminal. ~$tran If everything was done correctly then you should be greeted with the message which we console.logged in the file. index.js Something like this: All good! Handling Command Line arguments Our basic CLI is ready and now we move to adding further functionality. The most basic task that any CLI does is . In our CLI, we will be receiving the language name and the sentence to be translated as arguments and then we will parse it. handling command-line arguments Although Node.js offers built-in functionality for handling command line arguments, we are going to use an npm package called 🏴☠ which is specifically made for building CLIs. yargs will simplify our process of parsing arguments and help us organize command line flags. yargs 1. Install yargs ~$npm i yargs 2. After installing it, include the module in your : index.js yargs = ( ); const require "yargs" 3. Then create the object containing all your command line flags: options usage = ; options = yargs .usage(usage) .option( , { : , : , : , : }) .help( ) .argv; const "\nUsage: tran <lang_name> sentence to be translated" const "l" alias "languages" describe "List all supported languages." type "boolean" demandOption false true In the above code, I have defined an option which, when passed will print all the supported languages by the API, we will implement this later. Yargs provides us with and flags by default. -l --help --version If you want an option to be compulsory then you can set it’s value to , this will get yargs to throw a error if the flag is not provided. demandOption true Missing argument Testing it: Nice! All the arguments that you pass with the command gets stored under the list unless the argument begins with a — or a in that case it is treated as a flag with a default value of boolean. You can console.log yargs.argv to get a better picture about how the arguments are stored. yargs.argv._ -- Access the value of the passed flags using . yargs.argv.flagname Adding Utility Functions Now it’s time to add utility functions. I plan to take input as: ~$tran lang_name the sentence to be translated So we will need to parse the arguments. We can write all the utility functions in our but that wouldn’t look neat so I will make a separate file for all functions. Here’s what we need to do: index.js utils.js Create another file called in the bin folder. utils.js Include the file in your : index.js utils = ( ) const require './utils.js' 3. Create a function for parsing the sentence: Write the function in and then export it: utils.js .exports = { : parseSentence }; { sentence = ; ( i = ; i < words.length; i++) { sentence = sentence + words[i] + ; } module parseSentence ( ) function parseSentence words var "" for var 1 " " Call it in : index.js sentence = utils.parseSentence(yargs.argv._); var 4. Create a function to show help when no argument is passed: Create a function in your : utils.js .exports = { : showHelp, : parseSentence }; usage = ; { .log(usage); .log( ) .log( + + + ) .log( + + + + ) .log( + + + ) } module showHelp parseSentence const "\nUsage: tran <lang_name> sentence to be translated" ( ) function showHelp console console '\nOptions:\r' console '\t--version\t ' 'Show version number.' '\t\t' '[boolean]\r' console ' -l, --languages\t' ' ' 'List all languages.' '\t\t' '[boolean]\r' console '\t--help\t\t ' 'Show help.' '\t\t\t' '[boolean]\n' Call it in : index.js (yargs.argv._[ ] == ){ utils.showHelp(); ; } if 0 null return 5. Write a function in to show all supported languages: utils.js .exports = { : showAll, : showHelp, : parseSentence}; { .log(chalk.magenta.bold( )) ( [key, value] languages) { .log(key + + value + ) } } languages = (); languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) languages.set( , ) module showAll showHelp parseSentence ( ) function showAll console "\nLanguage Name\t\tISO-639-1 Code\n" for let of console "\t\t" "\n" let new Map 'afrikaans' 'af' 'albanian' 'sq' 'amharic' 'am' 'arabic' 'ar' 'armenian' 'hy' 'azerbaijani' 'az' 'basque' 'eu' 'belarusian' 'be' 'bengali' 'bn' 'bosnian' 'bs' 'bulgarian' 'bg' 'catalan' 'ca' 'cebuano' 'ceb' 'chinese' 'zh' 'corsican' 'co' 'croatian' 'hr' 'czech' 'cs' 'danish' 'da' 'dutch' 'nl' 'english' 'en' 'esperanto' 'eo' 'estonian' 'et' 'finnish' 'fi' 'french' 'fr' 'frisian' 'fy' 'galician' 'gl' 'georgian' 'ka' 'german' 'de' 'greek' 'el' 'gujarati' 'gu' 'haitian creole' 'ht' 'hausa' 'ha' 'hawaiian' 'haw' // (iso-639-2) 'hebrew' 'he' //or iw 'hindi' 'hi' 'hmong' 'hmn' //(iso-639-2) 'hungarian' 'hu' 'icelandic' 'is' 'igbo' 'ig' 'indonesian' 'id' 'irish' 'ga' 'italian' 'it' 'japanese' 'ja' 'javanese' 'jv' 'kannada' 'kn' 'kazakh' 'kk' 'khmer' 'km' 'kinyarwanda' 'rw' 'korean' 'ko' 'kurdish' 'ku' 'kyrgyz' 'ky' 'lao' 'lo' 'latin' 'la' 'latvian' 'lv' 'lithuanian' 'lt' 'luxembourgish' 'lb' 'macedonian' 'mk' 'malagasy' 'mg' 'malay' 'ms' 'malayalam' 'ml' 'maltese' 'mt' 'maori' 'mi' 'marathi' 'mr' 'mongolian' 'mn' 'burmese' 'my' 'nepali' 'ne' 'norwegian' 'no' 'nyanja' 'ny' 'odia' 'or' 'pashto' 'ps' 'persian' 'fa' 'polish' 'pl' 'portuguese' 'pt' 'punjabi' 'pa' 'romanian' 'ro' 'russian' 'ru' 'samoan' 'sm' 'scots' 'gd' //gd gaelic 'serbian' 'sr' 'sesotho' 'st' 'shona' 'sn' 'sindhi' 'sd' 'sinhalese' 'si' 'slovak' 'sk' 'slovenian' 'sl' 'somali' 'so' 'spanish' 'es' 'sundanese' 'su' 'swahili' 'sw' 'swedish' 'sv' 'tagalog' 'tl' 'tajik' 'tg' 'tamil' 'ta' 'tatar' 'tt' 'telugu' 'te' 'thai' 'th' 'turkish' 'tr' 'turkmen' 'tk' 'ukrainian' 'uk' 'urdu' 'ur' 'uyghur' 'ug' 'uzbek' 'uz' 'vietnamese' 'vi' 'welsh' 'cy' 'xhosa' 'xh' 'yiddish' 'yi' 'yoruba' 'yo' 'zulu' 'zu' Here I have created a to map all the language names to their code. This will , firstly it will help display all languages when needed, secondly, the API only takes the language code so even if the user enters the language name we can swap it with the language code before passing it to the API. Sneaky! 🤫. The swap would be in since we are using a hash map. hash map ISO-639–1 serve two purposes constant time Call the function in your if the or flag is true: showAll() index.js -l --languages (yargs.argv.l == || yargs.argv.languages == ){ utils.showAll(); ; } if true true return 6. Now write the function to do the dirty deed we talked about in your : utils.js .exports = { : parseLanguage, : showAll, : showHelp, : parseSentence }; { (language.length == ){ language; } (languages.has(language)){ languages.get(language) } { .error( ) ; } }; module parseLanguage showAll showHelp parseSentence ( ) function parseLanguage language if 2 return if return else console "Language not supported!" return //returning null if the language is unsupported. Convert the language to lower case and then call the function in : index.js (yargs.argv._[ ]) language = yargs.argv._[ ].toLowerCase(); language = utils.parseLanguage(language); if 0 var 0 // stores the language. //parsing the language specified to the ISO-639-1 code. 7. Now check if the sentence is empty, if not send it to the API: Include the API at the top of your : index.js translate = ( ); (sentence == ){ .error( ) .log( ) ; } translate(sentence, { : language}).then( { .log( + + res.text + + ; }).catch( { .error(err); }); const require '@vitalets/google-translate-api' if "" console "\nThe entered sentence is like John Cena, I can't see it!\n" console "Enter tran --help to get started.\n" return to => res console "\n" "\n" "\n" "\n" => err console Your CLI is complete now! One thing more that you can do is to decorate the output and errors with boxes and colors, we can do that using and . boxen chalk Beautification using Boxen and Chalk We can use terminal colors using and boxes to decorate our output using . chalk boxen 1. Install chalk and boxen. npm install chalk boxen 2. Include them in your and index.js utils.js chalk = ( ); boxen = ( ); const require 'chalk' const require 'boxen' 3. Add color to the usage constant. usage = chalk.hex( )( ); const '#83aaff' "\nUsage: tran <lang_name> sentence to be translated" 4. Display the output using a beautiful box. translate(sentence, { : language}).then( { .log( + boxen(chalk.green( + res.text + ), { : , : , : }) + ); }).catch( { .error(err); }); to => res console "\n" "\n" "\n" padding 1 borderColor 'green' dimBorder true "\n" => err console Feel free to explore both the packages and add customization to your heart’s content. :) The CLI in all its glory: Ahh yes Hope you had fun learning how to build your own and fully portable CLI :) because I had a lot of fun. Happy coding! The complete code for this can be found at: https://github.com/RushanKhan1/termTranslate Fork me on maybe :) Github Connect with me on . LinkedIn Also published at https://dev.to/rushankhan1/build-a-cli-with-node-js-4jbi