Node.js (To Do List App)
I’ve been seeing node being listed in all kinds of job descriptions and considering that I’m mostly familiar with javascript, I figured this could not be too hard to get into. I decided to build out a simple CLI To Do List app to get started with this runtime environment.
Modules
Let start with modules, what they are and how to use them. Modules is any file or directory in the node_module directory that can be loaded using Node.js ‘require’ function.
const chalk = require(‘chalk’)const yargs = require(‘yargs’)const notes = require(‘./notes’)
If you’re familiar with packages, you know that after installing them you need a way for your file to access them. In React.js, usually you do something like ‘import react from ‘react’ ‘. Similarly in Node.js, you create a variable and then require the name of the package. So the format should look something like…
const <package name> = require(‘<package name>’)
You can also import other files in a similar fashion but instead of requiring the name of the package, use the path of the file you’d like to access.
Inputs from your client
Node has a global object called process that provides information about, and controls, the node process. What we are interested in is getting a clients input, so how do we do that?
We use process.argv which stands for argument vector. If you console log this you’ll see something like …
console.log(process.argv)[‘/Users/Johnson/.nvm/versions/node/v11/bin/node’, ‘/User/Desktop/Johnson/node-course/notes-app/app.js’]
The first element is a path to your node executable on your machine, while the second path is a path to the file you’re working on. This is great and all but where does the input show? It shows here in the vector! How do you get it to show? Well first you need to understand how to run your node.js file. To run your file you simply have to enter
node <nameOfApp.js>
The cool thing is that you can add more information as argument in this line that will show up inside of your process.argv.
Step 1. node nameOfApp.js Johnson Step 2.
console.log(process.argv)//
[‘/Users/Johnson/.nvm/versions/node/v11/bin/node’, ‘/User/Desktop/Johnson/node-course/notes-app/app.js’,
'Johnson']
You’ll see that the extra information that you provided will get appended to the argument vector which is great because now we know where our input is stored.
Commands
With what we know now, lets create a simple application that lets us print a status of whether we are accessing the correct command and how to add more information.
const command = process.argv[2]console.log(process.argv)if(command === ‘add’){
console.log('Adding Notes!')
}else if(command === 'remove'){
console.log('Removing Note')
}In terminal lets run
node app.js add --title="This is a new title"
So what we’re doing here is checking to see that the commands correspond to the first argument we set in our terminal. If they match then we print something. In this case, we’d like to add a note with a title, which is new information. As you probably guessed, we’ll be able to find the information in the argument vector and it will look something like this…
[“/Users/Johnson/.nvm/versions/node/v11/bin/node”,
”/User/Desktop/Johnson/node-course/notes-app/app.js”,
“add”,
"- - title=This is a new title”]
The 4th element is exactly what we added as new information in the terminal which is great but it isn’t parsed in the way we want it to be so we’re going to have to manually parse it to get it to like an object.
Yargs
Yarg is one of the most popular parsing packages and it’ll be the one we use for this application. To install run ‘ npm i yargs’ to download the module into your project. Once that is complete, remember that we still need to use the require method to actually use it as a variable in our application. So let’s check out how yargs is used to parse our input.
Lets console log two thingsconsole.log(process.argv)
console.log(yargs.argv)in the command line let's run the add command to see how they differnode app.js add --title="Things to buy"OUTPUT[‘/Users/Johnson/.nvm/versions/node/v11/bin/node’, ‘/User/Desktop/Johnson/node-course/notes-app/app.js’,
'add',
'--title=Things to buy']{_:['add'], title:'Things to buy', '$0':'app.js'}
You can see that yargs did the heavy lifting for us and parsed our add command such that we have an object with a key of ‘title’ and a value of ‘Things to buy’. Great! Now we are essentially in a good spot to start building the add command for our app!
yargs.command({
command:'add',
describe: 'Enter description about command',
handler: function(){
console.log('adding a new note')
}})
Using the command method in yargs is used to define the variable that runs the command, a small description of what it does, and runs a function called a handler. For now, the handler just prints a message but what we’ll want it to do eventually is to take some kind of information and store it somewhere. If you ran this code right now with ‘node app.js add’, you’ll get the message ‘adding a new note’ which is perfect. You’re accessing your handler exactly as you should be. So let’s define the keys that we want out client to fill out before saving it.
yargs.command({
command:'add',
describe: 'Enter description about command',
builder: {
title:{
describe:"Note Title",
demandOption:true
type:"string"
},
body:{
describe:"Note Body",
demandOption:true
type:"string"
},
handler: function(){
console.log('adding a new note')
}})
The builder sets up the parameters that will be filled out by the client. Right now we’re saying that a note is comprised of a title and a body. DemandOption is a validator that makes is so that a note cannot be made without a title or a body. The type key restrict the input to a string meaning that a note cannot be made if the information is any other type like integers or booleans.
Great, so now we’ve got to create a file that hold all of our information. Preferably it will be a .json file and we will also create another file called ‘notes.js’ that will hold all the logic needed for out add command.
- notes.json
[]- notes.js
const fs = require('fs')
Right now, notes.json is an empty array as we have not added any notes to it. Remember, each object should look like {title: ‘Insert Title’, body:’Add Description’}.
First let’s create some helper functions inside of notes.js because we’ll need to access the notes that exist inside of notes.json, if there are any. We’ll also create another helper method to update notes.json with the updated information.
We’ll be making great use of ‘fs’ or file system module that will allow us to read and write to notes.json.
-notes.jsconst loadNotes = () => {
try{
const dataBuffer = fs.readFileSync('notes.json')
const dataJSON = dataBuffer.toString()
return JSON.parse(dataJSON)}
}catch(e){
return []
}const saveNotes = (notes) => {
const dataJSON = JSON.stringify(notes)
fs.writeFileSync('notes.json', dataJSON)
}
The loadNotes() function willl provide you with all the notes in your notes.json file and if there aren’t any notes, you’ll start with an empty array.
The savedNotes() function will take an array of object, notes, make it into a string and write to the notes.json file with the new information.
Finally, we are good to start creating the add command. We have a way to get out notes and a way to update our notes. All that is left to do is to add the note and we’re done!
Before we start, let’s break down what we need in this function. We need parameters (title and body). Remember, we can’t create a note with a title and body so we’ll make them our input arguments. We need to load or notes because we need to check for duplicates. We wouldn’t want to have two of the same things in our toDoList. Lastly, we need to save it and somehow visually show that we did in the cli.
const addNotes = (title, body) =>{
const notes = loadNotes();
const duplicateNotes = notes.find(note => note.title === title) if(!duplicateNotes){
notes.push({
title:title,
body:body
})
savedNotes(notes)
console.log('New Note Added')
}else{
console.log('Note Already Exists')
}
}
Congratulations! You’ve just build out a big portion of the toDoList! addNotes accepts a title and body as their argument. It loads all the existing notes inside of the notes.json and then immediately checks if the current input already exists another note inside your list.
If the duplicateNote does not exist, then we want to push the new object into the notes. Then we call on our savedNotes function to update our notes.json file with our new array of notes. We console log a success message if duplicate does not exist and an error message if the duplicate note does exist!
With this foundation, you should now be able to create three more commands. These commands are remove, read, and list. Remove will remove a note given a title, read will give you the body of a note with a specified title, and lastly list will print the title of all existing notes.
I highly recommend using the Node.js documentation if you get lost! For each method and command, break down the process of what you need to for a command. For example, let’s create a game plan for the remove function.
- Create the method remove that takes a title as an argument.
- Load the notes (which is an array of object)
- use an iterator to return all notes except for the note that has the title argument.
4. You can either have a match or not. If you do have a match use saveNotes() to update the notes.json file. If you do not have a match then no updates are required as the note does not exist.
I hope you enjoyed this walk through. I’m learning node.js now and I’m having a blast since js is the syntax that I’m most familiar with at this point. As always, happy coding!