Of the many early projects there are to build while working/learning with NodeJS, the command line is a tool common to all. According to google (well, if it's not already general knowledge) the command line is a computer environment (same as a command prompt) and an easy way to test simple Nodejs/JavaScript code and allows execution of multiple lines of such codes. Put simply, we (as devs) can run REPL (Read-Eval-Print Loop) instructions on the command line using nodeJs syntax in the command prompt.
In this write-up, one of my many aims is to walk you through how to create a simple notes app with the use of NodeJS and its conventional approach of "process.argv", but the main goal is how we get the same result using the Yargs module, and also to understand the basics and setup involved in achieving this.
The concept of a notes app is one we are all familiar with, and the functions we are possibly looking to recreate include – Adding notes, Deleting a note, List Notes, and Reading from a note. First, we go through a summary of the conventional approach with the use of NodeJS's Process.argv.
Conventional Approach – process.argv
The process argument vector (process.argv for short) property when logged to the console returns an array that includes the command-line arguments passed when the Node.js process was launched. The first element in a process.argv is the path to NodeJS on the machine (known as the process.execPath), The second element will be the path to the JavaScript file being executed, and any other elements after (in the same array) are additional command-line arguments.
// create an app.js file in a notes-app folder
console.log(process.argv)
// run the app.js file on the terminal with an argument
// using "node app.js '--addTask' --task='Grocery shopping'"
// terminal code
node app.js '--addTask' --task='Grocery shopping'
// output
[
'C:\Program Files\node\node.exe',
'C:\Users\User\Desktop\notes-writeup\app',
'--addTask',
"--task='Grocery shopping'"
]
Since we are using the process.argv approach and our output are in the form of an array, it then is proper to use array indexing to pull out the required command from the third argument passed, which would possibly use the fourth argument (as shown above), we can then run an if/else statement or use a switch approach as related to the project we are working on (which in this case is the Notes App).
console.log(process.argv);
if( process.argv[2] === '--addTask') {
//we pass into a custom function we create
handleTaskAdd(process.argv[3]);
} else if ( process.argv[2] === '--removeTask') {
handleTaskRemove(process.argv[3]);
} else {
... some other condition
}
// where the -- is used as a convention.
In our use of this approach, we can already point out a couple of issues of which the multiple if/else isn't at the top of that list. Let me help you get there faster if you still haven't. Some of the issues with this approach include:
Issues with Array nesting and index extraction (what happens if the wrong argument is passed at a particular index).
Limited control over commands and how they are used in the prompt.
Error checks on data passed, and the descriptive error message returned.
Lack of descriptive helpers (We need something like a tooltip to remind us of commands available, and possibly what they do).
Setting required fields, and how to enforce that (what if a user uses the ‘--add’ command, but leaves the parameters empty)?
Proper string and command formatting.
These and many more can be found as bottlenecks of the application usage and will force us to focus more on bugs, and handling errors than the progress of the project itself.
Solution ?... Ladies and Gentlemen, I give you Yaargs (it's just one ‘a’ though).
YARGS
Keeping it simple, Yargs helps to build interactive command line tools, by parsing arguments and generating an elegant user interface (yes, I do declare that I copied this from their npm documentation. Oops, - how very piratey of me). Now follow me as we take a dive into this tool, and explore how it assists us in parsing and other loots (get the pirate reference?) that it promises.
Like all great apps, after setting up npm init -y, we also have to install the required modules, and to do that for yargs is to run the command :
npm install yargs
Next, just like process.argv we want to print to the console yargs.argv. By printing this, we get to view our arguments and structure as we build.
To start us off on our usage and creating bare bones for the notes app, we are going to go over some basic setups, which include:
- Creating command
- Using a builder object
- Setting up a handler method
- Passing in arguments to handlers
.
1. Create Command.
As stated, one of the aims of this write-up is to create a Notes App, which will then prove helpful in illustrating points and help us pass across clear definitions of usage. In order to have this Notes App functional, we’ll first go over creating the ‘add note’ command using yargs. Commands are clear-cut ways to tell our Notes project what to expect and how to handle the results of these expectations. The yargs command is a method that takes an object as its arguments, this object includes values like the command keyword, describe handler (a function that runs when the command is used), and the builder (here we handle parameters that the command is to watch out for).
To create a command :
Yargs.command( {
command: ‘add’,
describe: ‘add a new note’,
// the describe adds a description of what the command does
})
2. Create a builder Object.
The builder object located in the command is how we create the required fields in the app. For example, in a Notes App, we’d require a note title and also a note body, both of which are required. In other to make this work, we add a builder object into the object that gets passed into yargs.command as an argument.
Yargs.command( {
command : ‘addNote',
describe: ‘add a new note’,
builder: {
//to create a title field
title: {
describe: ‘Note title’,
demandOption : true,
//the demand option helps to set the title field as required
type: ‘string’
// The type returns a string if no value is passed,
// it's a Boolean by default
// It is mostly used in cases where a field is not required.
},
//end of the title field
body: {
describe: ‘Note body’,
demandOption : true,
type: ‘string’
}
//end of the body field
},
//end of builder object
})
console.log(yargs.argv)
3. The Handler Method
The handler is a function that runs for a required command, with a basic knowledge of JavaScript, this command can be utilized however required.
Yargs.command( {
command: ‘addNote’,
describe: ‘add a new note’,
builder: {
//to create a title field
title : {
describe: ‘Note title’,
demandOption : true,
//the demand option helps to set the title field as required
type: ‘string’
// The type returns a string if no value is passed,
// it's a Boolean by default
// It is mostly used in cases where a field is not required.
},
//end of the title field
body: {
describe: ‘Note body’,
demandOption : true,
type: ‘string’
}
//end of the body field
},
//end of builder object
handler : ()=> {
console.log(‘Adding a new note’)
//you can call a custom function here
}
})
console.log(yargs.argv)
The snippet above is how a basic command is created, we can run node app –help to see these commands listed and their descriptions (that is when multiple commands like the delete, list, and read have been set up). Also note that one can set as many fields as required into the builder, and the console call to yargs.argv should be done at the end of the App. With our snippets so far we can see our progress and how it fares better compared to the conventional approach. If we’ve successfully made it this far, we are now ready for our next phase, passing arguments to handlers.
4. Handlers and Arguments.
At this point, we are probably wondering how we have or get access to the values attached to the functions created in the handler. In each yargs command, we are provided with an argv argument which the handler has an access to (arguments after the command specified are taken as an input into the argv for that command), and in other to use this, we pass the argv into our handler, which can then be fed into the function we intend to run. Taking a look at just the terminal and handler part of the code, we now have :
Terminal code
node app.js addNote --title="Lecture Assignment" --body="Turn in page 1 through 3"
// ... codes written before the addNote handler
handler : (argv) => {
console.log(argv)
// here we now have access to the title and body arguments.
// we can (also destructure from argv) pass this value to a function we create e.g
handleAddNote(argv)
}
In the argv, we have access to the title and body. Actually, we have access to all arguments after the add command is run, take note to use the right format : --title = " A title".
To check all available commands, you can run node app --help in the console, it's a utility available on yargs. These pretty much wrap up all our functionalities and setup, what's left is to actually combine the pieces, and make it whole (That is building the actual project and including the file system module).
To see the full project of the Notes App, do check out the repo : link to Github repo Some dummy data has been created in the notes.json to work off. Also, don't forget to npm install
Conclusion
When it comes to being a developer basics must be understood before modules/packages that make coding easier are explored. This approach helps us to understand tradeoffs and provides us with wider application and flexibility in tool usage. While process.argv can help create a working notes app (which can be adapted to other usages) the use of yargs helps us to overcome certain shortcomings and shifts our focus from a bug-filled approach to one which is more focused on ease of delivery and readability. I truly hope this piece has helped you in one way or another to understand the differences and similarities between process.argv which is a core node module, against yargs and how both can be combined to achieve your desired result. Do feel free to learn more about this module and the many other features it provides.
Thank you for your time ❤❤.