NodeJS 8 from Scratch — Part 2

Anuj Baranwal
7 min readJul 13, 2018

--

In previous part, we discussed about(https://medium.com/@anujbaranwal/nodejs-8-from-scratch-part-1-a3c1431f1e15) —

  • nodeJS from basics
  • the essentials that is needed to build nodeJS applications
  • understand how node works
  • the two important questions — What is node? and Why node?

In this part, we will discuss further about node basics and create a command line based notes application from scratch in node

Repo used in this part — https://github.com/anuj070894/nodejs_medium_1

Before we go any further, it is important to understand that a node application can be divided into separate files separating the concerns and keeping similar functions together. And if someone needs it, it can import it via require

require

A function available in file that serves three main purposes

  • importing in-built node modules
  • third party node module
  • user file

importing in-built node modules

Let’s see a simple example of writing a simple line Hello, I am Anuj where the Anuj is the username of the os. We will use fs and os to accomplish this task. fs and os are in-built to node. Put it in some app.js file and run node app.js

const fs = require('fs');const os = require('os');const username = os.userInfo().username;const greetingString = `Hello I am ${username}`; // templating strings. Appending strings using "+" gets difficult to read laterfs.writeFileSync('greeting.txt', greetingString);

Branch — 01_Init

third party node module

Let’s work on a sample that imports a third party module. Here we will make use of lodash

We need to initiate a node app first. To do that, we run npm init , it will create a package.json file for us which maintains the package related information for the app. To install lodash , run

npm install --save lodash

in sample_proj/app.js

const _ = require('lodash');const arr = [1,2,3,4,5];console.log(_.reverse(arr)); // [ 5, 4, 3, 2, 1 ]

user file

Now, the last thing in require is to see how requiring a user file works. It is always relative to where the file is located to the file where it is being imported.

in sample_proj/app2.js

console.log('app2.js');const notes = require('./notes');
// console.log(notes); // { a: 2 }

in sample_proj/notes.js

console.log("Notes.js");// module.exports = { a: 2 };// console.log(module); // will discuss this below

If you see the output by logging the module(a big object) in notes.js, you will see an exports object which will be empty. This is what gets assigned to notes in app2.js when we require notes in app2.js

Branch — 02_Init

Nodemon

Running node app2.js again and again gets not so fun sometimes and therefore we can make use of a module called as nodemon on a file and it watches for files and re-runs the node process again whenever there are changes made. Install — npm install -g nodemon

To run this — nodemon app2.js

In this part, we will be building a command line based notes application. A user can put this on terminal —

npm init and accept all the defaults for now.

Sample Command to be run by user — node app.js (add/remove/list/read) --title="Title" --body="Body"

to add a note, remove a note, list all notes, read the body of a note by title.

User should provide both title and body when adding a note, title when remove/reading a note, and nothing while listing all notes

So, let’s get started

We can make use of process.argv to accomplish this

console.log(process.argv); 
/*
node index.js add --title="anuj"
[ '/Users/anujkuma/.nvm/versions/node/v8.10.0/bin/node','/Users/anujkuma/Projects/teaMOOC/Nodejs/NodejsUdemy/Projects/hello_world/notes/index.js','add','--title=anuj' ]*/

As you can see above the process.argv doesn’t return a well formatted output for — — title=”Anuj”

This is a good time we make use of npm module like yargs which does some amazing things with command line arguments

npm install --save yargs@4.7.1

For the same command run above with yargs.argv,

{ _: [ 'add' ], title: 'anuj', '$0': 'index.js' }

The output looks much more easier to work with

npm init — to initialise a project called Notes

in notes/index.js

const yargs = require('yargs');const notes = require('./notes');const argv = yargs.argv;const command = argv._[0];if (command === 'add') {const note = notes.add(argv.title, argv.body);if (note) {console.log('Note has been successfully added!!!');notes.logNote(note);} else {console.log(`Note with ${argv.title} already exists.`);}} else if (command === 'remove') {const noteRemovedSuccess = notes.remove(argv.title);if (noteRemovedSuccess) {console.log(`Note with title ${argv.title} successfully removed`);} else {console.log(`Note with title ${argv.title} not found`);}} else if (command === 'list') {notes.list();} else if (command === 'read') {const note = notes.read(argv.title);if (note) {console.log('Note found!!!');notes.logNote(note);} else {console.log(`Note with title ${argv.title} not found`);}} else {console.log("Command not recognised!!!");}

in notes/notes.js

const fs = require('fs');const fetchNotes = () => {try {const allNotes = fs.readFileSync('notes-data.json');const allNotesParsed = JSON.parse(allNotes);return allNotesParsed;} catch (e) {return [];}}const writeNotes = (allNotes) => {fs.writeFileSync('notes-data.json', JSON.stringify(allNotes));};const logNote = (note) => {console.log('--');console.log(`Title: ${note.title}`);console.log(`Body: ${note.body}`);}const add = (title, body) => {const allNotes = fetchNotes();const note = {title,body};const duplicateNotes = allNotes.filter((note) => note.title === title); // note with title same as current title. If there is a duplicate already there, we should not add itif (duplicateNotes.length === 0) {allNotes.push(note);writeNotes(allNotes);return note;}}const remove = (title) => {const allNotes = fetchNotes();const filteredNotes = allNotes.filter((note) => note.title !== title);if (filteredNotes.length === (allNotes.length - 1)) {writeNotes(filteredNotes);return true;}return false;}const read = (title) => {const allNotes = fetchNotes();const filteredNote = allNotes.filter((note) => note.title === title);if (filteredNote.length === 1) {return filteredNote[0];}}const list = () => {const allNotes = fetchNotes();console.log(`Total Notes: ${allNotes.length}`);allNotes.forEach((note) => {logNote(note);});}module.exports = {add,remove,read,list,logNote};

Branch — 03_Init

It is important to understand how we can debug node apps. There are two ways to do this. Node version of 8 and above is required to debug node apps

  • debugging through command line
  • debugging through chrome

Command Line

Run node inspect app.js

Some useful shortcuts while debugging via command line —

  • list(n) — lists out the n lines of code from the current line in context
  • n — go to next step
  • c — continue till the next debugger has been hit
  • repl — will put the node in a read-eval-print-loop state and can inspect different variable values at the point
  • debugger — we do not want to press n continuously to go to the point where we want to go. Therefore, we can put statements like “debugger” in between the code and press c, it will stop the execution at that point

The same thing can be done with nodemon

nodemon inspect index.js add --title="sell" --body="oranges"

However, sometimes it feels a little too much to do when debugging via the command line. Therefore, there is also a way to debug node apps via gui based interface in chrome. You need a chrome browser and node v8 to do this

Debugging through chrome

Run node --inspect-brk index.js add --title="Sell" --body="Oranges"

Go to chrome://inspect/#devices

Click Open Dedicated DevTools for Node

You will get a nice interface where you can debug in it just like you debug any web based javascript application in the browser

We can achieve the same thing with nodemon

Run nodemon --inspect-brk index.js add --title="Sell" --body="Oranges"

As we have seen earlier the usage of yargs to do the cumbersome tasks of extracting the params out of the command line arguments passed to it. It can also be used to provide the necessary description, help and aliases for the command line arguments.

yargs
.command('add', 'Add a note', {
title: {
describe: 'Title of note',
demand: true,
alias: 't'
},
body: {
describe: 'Body of note',
demand: true,
alias: 'b'
}
})
.help()
.argv;
  • add is the command here with a small description about it. And the arguments that can be passed to it, e.g., title has three keys —

describe — small description about the argument

demand — whether or not it is a required argument or not

alias — shortcut to use this argument

node index.js add -t="hello" -body="I am Anuj"

Branch — 04_Init

Arrow functions

Since, we will be using object literals a lot in the series. It will be good if we talk a bit about how arrow functions behave when they are put as value to an attribute inside an object literal. They behave a bit weirdly inside object literals when arrow function is used. So, if arguments and this is needed for a function, it is advised to use an alternative syntax for arrow function

var user = {
name: 'Anuj',
hiFn: () => {
console.log(arguments); // exports, require, module, __filename, __dirname
console.log(`Hello I am ${this.name}`); // Hello I am undefined
},
hiFnAlt () {
console.log(arguments); // prints what is passed to the function. if hiFnAlt is called with (1,2,3) then arguments will be { 0: 1, 1: 2, 2: 3}
console.log(`Hello I am ${this.name}`); // Hello I am Anuj
}
}

So, this is pretty much what we will be discussing in this part. Stay tuned for next parts.

In this part, we discussed about —

  • require
  • nodemon
  • yargs
  • debugging
  • arrow functions

Thanks and keep learning :)

Part 3 — https://medium.com/@anujbaranwal/nodejs-from-scratch-part-3-20956ec252a3

Part 4 — https://medium.com/@anujbaranwal/nodejs-from-scratch-part-4-d0aadf019c79

Part 5 — https://medium.com/@anujbaranwal/nodejs-8-from-scratch-part-5-3d3e3a84b64

Part 6.1 — https://medium.com/@anujbaranwal/nodejs-8-from-scratch-part-6-1-api-development-5dee11785d62

Part 6.2 — https://medium.com/@anujbaranwal/nodejs-8-from-scratch-part-6-2-api-development-f92f76eb3521

Part 6.3 — https://medium.com/@anujbaranwal/nodejs-8-from-scratch-part-6-3-api-development-9b046fed7364

Part 6.4 — https://medium.com/@anujbaranwal/nodejs-8-from-scratch-part-6-4-api-development-38d600a35ad9

--

--

Anuj Baranwal

Full Stack Web Developer @AdobeSystems Bangalore. Sometimes I code, I draw, I play, I edit and write