Technology

Learn Node.js in 90 minutes

Dipesh Patel

I am writing this blog to help beginners learn and start developing Node.js application. This blog post is based on the meetup conducted by me on how anyone can start working and building basic Node.js application in 90 minutes.

In this post you will learn and get familiar with following things:

  • What is Node.js?
  • Why any body should go with Node.js?
  • Installing Node.js
  • Understanding NPM
  • Bootstrap Node.js application
  • Understanding & creating Modules
  • Understanding Event Loop
  • Using Express Framework
  • Writing Unit Test in NodeJS
  • Working with Session & Database
  • Building sample API

What is Node.js?

Node is just JavaScript without a browser. Node wasn’t just built from scratch, it came from another project. Node is actually based of the V8 JavaScript run time from Google Chrome. Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient.

Node.js is having some more functions added to it and has removed some of the functions and object that are available in Google Chrome.

Objects not available in Node.js are

  • window
  • document
  • location

This objects are not available in Node.js because Node.js application works on server side and does not have to deal with UI and URL handling. Instead of this object Node.js has introduced “global” object which holds the global stat across one Node.js process.

Objects not Available in chrome is

  • global

Objects available and common in both

  • console

Why any body should go with Node.js (Advantages of Node.js)?

Node.js is in contrast to today’s more common concurrency model where OS threads are employed, uses event driven programming. Thread-based networking is relatively inefficient and very difficult to use. Furthermore, users of Node are free from worries of dead-locking the process—there are no locks. Almost no function in Node directly performs I/O, so the process never blocks.

The way Node.js handles event-driven programming makes it helpful for handling things like streams and sockets and very large files.

As Node.js is non-blocking, which makes less-than-expert programmers are able to develop scalable applications.

Easy to adapt
Node.js makes it possible for you to take advantage of everything Javascript has to offer without being tied to a browser.

Hosting
With rapid adoption, world-class Node.js hosting is also proliferating. In particular, Platform-as-a-Service (PaaS) providers such as Modulus and other reduce deployments to a single command. Even the granddaddy of PaaS, Heroku, now formally supports Node deployments.

Easy to build Real Time Application & Monitoring
You can easily build real time application using most widely used “Socket.io” library in Node.js

Monitoring & Profiling of application is very using various Process manager available in Node.js

High-Performance
PayPal reported: double the number of requests per-second and reduced response time by 35% or 200 milliseconds.

WalMart Labs had a bumper launch with Node.js in 2013, where they put all of their Mobile traffic through Node.js on black-friday, the busiest shopping period of the year.
The team at WalMart Labs live tweeted against #nodebf tag showing the performance of the Node.js application.

On Black Friday the WalMart servers didn’t go over 1% CPU utilisation and the team did a deploy with 200,000,000 users online.

Similarly Groupon re-implemented their system in Node.js which resulted in page load times dropping by a whopping 50%.

The old story of linkedin where moving to Node.js from Rails for their mobile traffic, reducing the number ofservers from 30 to 3 (90% reduction) and the new system was up to 20x faster.

The Future of Node.js is bright
It is backed by a huge community and sponsored by big corporate Joyent

Node.js’s package ecosystem, npm, is the largest ecosystem of open source libraries in the world.

Eran Hammer announced at NodeDay that WalMart are planning to use Node.js across all eCommerce by the end of 2014.

PayPal are rolling out Node.js across their entire web application and Node.js will be used almost everywhere within 2 years.

Although Google have not publicly come out talking about their use of Node.js, there is evidence that they are using it, a simple search on linkedin shows almost 200 people in google with Node.js on their profile. According to an article on VentureBeat: “Googlers are definitely working on Node.js projects – perhaps even something big that will be released to the public in the near future. They’re just not ready to talk publicly about what that might be.”

Yahoo are a long way down the line with Node.js and have 200 developers doing Node.js full time with 800 public modules and 500 private ones.

New Entrants to Node.js such as Netflix and others show that there is big growth happening in this area at present.

Installing Node.js
You can install Node.js in following different ways.

  • Using per-compiled binary
  • Using Installers
  • By compiling source
  • Using NVM (Node Version Manager)

Lets see one by one each style of installing Node.js

Installing via pre-compiled binary & installer is very simple, you have to just go the Node.js download page and get the node binary or Installer (.exe or .dmg) file and following the instruction which installer gives you.

Compiling Source
Python 2.6 or 2.7 is required to build from source tarballs.

Follow the steps below to install the Node.js by compiling it

  • Download the source from Node.js site.
  • Extract the source in the folder and go inside that folder
  • Run following commands in sequence
    • ./configure
    • make
    • sudo make install

Using NVM

NVM is Node Version Manager, you can need to install NVM using following command first to install using curl
to install using curl

curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.30.1/install.sh | bash

to install using wget

wget -qO- https://raw.githubusercontent.com/creationix/nvm/v0.30.1/install.sh | bash

Add these lines to your ~/.bashrc, ~/.profile, or ~/.zshrc file to have it automatically sourced upon login:

export NVM_DIR=”$HOME/.nvm”
[ -s “$NVM_DIR/nvm.sh” ] && . “$NVM_DIR/nvm.sh” # This loads nvm

Once you install NVM life becomes very easy for installing and maintaining different versions of Node.js
To download, compile, and install the latest v5.0.x release of node, do this:

nvm install 5.0

And then in any new shell just use the installed version:

nvm use 5.0

Or you can just run it:

nvm run 5.0 –version

Or, you can run any arbitrary command in a subshell with the desired version of node:

nvm exec 4.2 node –version

You can also get the path to the executable to where it was installed:

nvm which 5.0

For More information on NVM and its usage visit following link

https://github.com/creationix/nvm

Understanding NPM
npm is the package manager for Node.js called Node Package Manager. It was created in 2009 as an open source project to help JavaScript developers easily share packaged modules of code. The npm Registry is a public collection of packages of open-source code for Node.js, front-end web apps, mobile apps, robots, routers, and countless other needs of the JavaScript community. npm is the command line client that allows developers to install and publish those packages.npm, Inc. is the company that hosts and maintains all of the above.

To install any node module & save it as dependency as dependency without which application cannot run use following command

npm install <module_name> –save

To install any node module & save it as dependency as developer dependency which are required to run on developer machine, use following command

npm install <module_name> –save-dev

To uninstall the module, run following command

npm uninstall <module_name> –save

To install all the modules in developer machine which are listed in package.json file of the project, run following command

npm install

To install all the modules in production machine which are listed in package.json file of the project, run following command

npm install –production

Bootstrapping Node.js Application
To start working with fresh new application and bootstrap it you need to run following command

cd <source-folder>

npm init

Once you run this command it will ask you following question, just provide proper info and continue, your application will be ready to go.

  • name —It is the name of application, it should not contain blank space
  • version — version of your application, default is (1.0.0)
  • description — Full info about your application
  • entry point — Its the file which is the main entry point of your application
  • test command — The BASH command(s) to be executed when “npm test” command is executed
  • git repository — Endpoint of your GIT repo of the project
  • keywords — In case you are building NPM package this keywords will be used to index your application for search in NPM repository
  • author — Name of the author/person who is main contact person or module author.
  • license — If any license is applicable in case you are creating NPM package.

Once you answer all the question, it will prompt for confirmation, once you confirm “yes” a “package.json” file will be created in your folder with appropriate details and you are now ready to go.

Understanding & Creating Modules
Node.js has a simple module loading system. In Node.js, files and modules are in one-to-one correspondence. As an example, module-sample.js loads the module module1.js, module2.js, module3.js in the same directory and module4 which is in module4/index.js.


The contents of module-sample.js:

var module1=require('./module1.js');
module1();
var module2=require('./module2.js');
var prefix='Phd';
module2('Dipesh');
var module3=require('./module3.js');
module3.add(12,10);
module3.sub(12,10);
var module4=require('./module4');
module4.add(12,10);
module4.sub(12,10);


The contents of module1.js

'use strict';
module.exports=function(){
  console.log('Hello World');
};


The contents of module2.js

var prefix='Mr';
module.exports=function(name){
 console.log(`Hello ${prefix} ${name}`);
};


The contents of module3.js

'use strict';
function add(a,b){
 console.log(`${a} + ${b} = ${a+b}`);
}
function sub(a,b){
 console.log(`${a} - ${b} = ${a-b}`);
}

module.exports={
 add:add,
 sub:sub
};


The contents of module4/index.js

'use strict';
function add(a,b){
 console.log(`${a} + ${b} = ${a+b}`);
}
function sub(a,b){
 console.log(`${a} - ${b} = ${a-b}`);
}

module.exports={
 add:add,
 sub:sub
};

To add functions and objects to the root of your module, you can add them to the special exports object.

Variables local to the module will be private, as though the module was wrapped in a function. In this example the variable prefix is private to module2.js

If you want the root of your module’s export to be a function (such as a constructor) or if you want to export a complete object in one assignment instead of building it one property at a time, assign it to module.exports instead of exports, as shown in module4/index.js

The module system is implemented in the require(“module”) module.

To get the exact filename that will be loaded when require() is called, use the require.resolve() function, which loads the file based on following algorithm.

require(X) from module at path Y
1. If X is a core module,
 a. return the core module
b. STOP
2. If X begins with './' or '/' or '../'
a. LOAD_AS_FILE(Y + X)
b. LOAD_AS_DIRECTORY(Y + X)
3. LOAD_NODE_MODULES(X, dirname(Y))
4. THROW "not found"

LOAD_AS_FILE(X)
1. If X is a file, load X as JavaScript text. STOP
2. If X.js is a file, load X.js as JavaScript text. STOP
3. If X.json is a file, parse X.json to a JavaScript Object. STOP
4. If X.node is a file, load X.node as binary addon. STOP

LOAD_AS_DIRECTORY(X)
1. If X/package.json is a file,
 a. Parse X/package.json, and look for "main" field.
 b. let M = X + (json main field)
c. LOAD_AS_FILE(M)
2. If X/index.js is a file, load X/index.js as JavaScript text. STOP
3. If X/index.json is a file, parse X/index.json to a JavaScript object. STOP
4. If X/index.node is a file, load X/index.node as binary addon. STOP

LOAD_NODE_MODULES(X, START)
1. let DIRS=NODE_MODULES_PATHS(START)
2. for each DIR in DIRS:
a. LOAD_AS_FILE(DIR/X)
b. LOAD_AS_DIRECTORY(DIR/X)

NODE_MODULES_PATHS(START)
1. let PARTS = path split(START)
2. let I = count of PARTS - 1
3. let DIRS = []
4. while I &amp;amp;amp;amp;amp;amp;amp;gt;= 0,
 a. if PARTS[I] = "node_modules" CONTINUE
c. DIR = path join(PARTS[0 .. I] + "node_modules")
b. DIRS = DIRS + DIR
c. let I = I - 1
5. return DIRS

Modules are cached after the first time they are loaded. This means (among other things) that every call to require(‘foo’) will get exactly the same object returned, if it would resolve to the same file.

Multiple calls to require(‘foo’) may not cause the module code to be executed multiple times. This is an important feature. With it, “partially done” objects can be returned, thus allowing transitive dependencies to be loaded even when they would cause cycles.

If you want to have a module execute code multiple times, then export a function, and call that function.

Modules are cached based on their resolved filename. Since modules may resolve to a different filename based on the location of the calling module (loading from node_modules folders), it is not a guarantee that require(‘foo’) will always return the exact same object, if it would resolve to different files.

When there are circular require() calls, a module might not have finished executing when it is returned.

File Modules
If the exact filename is not found, then Node.js will attempt to load the required filename with the added extensions: .js, .json and finally .node

  • js files are interpreted as JavaScript text files
  • json files are parsed as JSON text files.
  • node files are interpreted as compiled add on modules loaded with dlopen

A required module prefixed with ‘/’ is an absolute path to the file. For example, require(‘/home/dpatel/foo.js’) will load the file at /home/dpatel/foo.js

A required module prefixed with ‘./’ is relative to the file calling require(). That is, bar.js must be in the same directory as foo.js for require(‘./bar.js’) to find it.

Without a leading ‘/’, ‘./’, or ‘../’ to indicate a file, the module must either be a core module or is loaded from a node_modules folder.

If the given path does not exist, require() will throw an Error with its code property set to MODULE_NOT_FOUND

Folders as Modules
It is convenient to organize programs and libraries into self-contained directories, and then provide a single entry point to that library. There are three ways in which a folder may be passed to require() as an argument.

The first is to create a package.json file in the root of the folder, which specifies a main module. An example package.json file might look like this:

{
 "name" : "some-library",
 "main" : "./lib/some-library.js"
}

If this was in a folder at ./some-library, then require(‘./some-library’) would attempt to load ./some-library/lib/some-library.js

This is the extent of Node.js’s awareness of package.json files.

If there is no package.json file present in the directory, then Node.js will attempt to load an index.js or index.node file out of that directory. For example, if there was no package.json file in the above example, then require(‘./some-library’) would attempt to load:

  • /some-library/index.js
  • /some-library/index.node

Understanding the Event Loop
Node’s “event loop” enable node to handle high throughput scenarios. It is the reason Node can essentially be “single threaded” while still allowing an arbitrary number of operations to be handled in the background.

To understand how event loop works, we need to understand Event Driven programming paradigm, event driven programming is widely used in UI applications.

Event driven programming is the application flow control that is determined by events or change in state. Its implementation is to have a central mechanism that listens for a event and call the callback function associated with it when event has been detected. This is the basic principle behind the node’s event loop

In client side’s javascript development. If you see, button.onClick(), this is called when user click on button. Similarly node handles the situation where same element emits multiple events using EventEmitter, which you can find in Socket, http, Server, etc modules.

Many objects in Node.js emit events, All objects which emit events are instances of events.EventEmitter. You can access this module by doing: require(“events”);

Typically, event names are represented by a camel-cased string, however, there aren’t any strict restrictions on that, as any valid property key will be accepted.

Functions can then be attached to objects, to be executed when an event is emitted. These functions are called listeners. Inside a listener function, this refers to the EventEmitterthat the listener was attached to.

There is one common misconception about Event Emitter is that it seems to be asynchronous in nature on their own, but this is not correct.

Let me show you the example to show this

var EventEmitter=require('events').EventEmitter;
var util=require('util');

function SampleEmitter() {
 EventEmitter.call(this);
}
util.inherits(SampleEmitter, EventEmitter);

var me = new SampleEmitter();

SampleEmitter.prototype.doCall = function doCall() {
 console.log('before');
 me.emit('fire');
 console.log('after');
};
me.on('fire', function() {
 console.log('emit fired');
});

me.doCall();

var fs = require("fs");
fs.readFile('package.json', function (err, data) {
 if (err){
 console.log(err.stack);
 return;
 }
 console.log(data.toString());
});
console.log("Program Ended");

EventEmitter often appears asynchronous because it is regularly used to signal the completion of asynchronous operations, but the EventEmitter API is entirely synchronous. The emit function may be called asynchronously, but note that all the listener functions will be executed synchronously, in the order they were added, before any execution can continue in statements following the call to emit.

In an event-driven application, there is generally a main loop that listens for events, and then triggers a callback function when one of those events is detected.

For more information visit blog: https://rapidops.com/blog/event-loop-with-javascript/

Using Express Framework
Express is a minimal and flexible Node.js web application framework that provides a robust set of features for web and mobile applications. With a myriad of HTTP utility methods and middleware at your disposal, creating a robust API is quick and easy. Express provides a thin layer of fundamental web application features, without obscuring Node.js features that you know and love.

You can see express API in more details at link http://expressjs.com/en/4x/api.html

Installing Express framework
Assuming you’ve already installed Node.js, create a directory to hold your application, and make that your working directory.

$ mkdir myapp
$ cd myapp

Use the npm init command to create a package.json file for your application. For more information on how package.json works, see Specifics of npm’s package.json handling.

$ npm init

This command prompts you for a number of things, such as the name and version of your application. For now, you can simply hit RETURN to accept the defaults for most of them, with the following exception:

entry point: (index.js)

Enter app.js, or whatever you want the name of the main file to be. If you want it to be index.js, hit RETURN to accept the suggested default file name.
Now install Express in the app directory and save it in the dependencies list. For example:

$ npm install express –save

To install Express temporarily and not add it to the dependencies list, omit the –save option:

$ npm install express

Creating Hello World Example

First create a directory named myapp, change to it and run npm init. Then install express as a dependency, as per the installation steps above.

In the myapp directory, create a file named app.js and add the following code:

var express = require('express');
var app = express();

app.get('/', function (req, res) {
 res.send('Hello World!');
});

app.listen(3000, function () {
 console.log('Example app listening on port 3000!');
});

The app starts a server and listens on port 3000 for connections. The app responds with “Hello World!” for requests to the root URL (/) or route. For every other path, it will respond with a 404 Not Found.

Run the app with the following command:

$ node app.js

Then, load http://localhost:3000/ in a browser to see the output.

Basic Routing in Express
Routing refers to determining how an application responds to a client request to a particular endpoint, which is a URI (or path) and a specific HTTP request method (GET, POST, and so on).

Each route can have one or more handler functions, which are executed when the route is matched.

Route definition takes the following structure:

app.METHOD(PATH, HANDLER)

Where:

  • app is an instance of express.
  • METHOD is an HTTP request method.
  • PATH is a path on the server.
  • HANDLER is the function executed when the route is matched.

Respond with Hello World! on the homepage:

app.get('/', function (req, res) {
 res.send('Hello World!');
});

Respond to POST request on the root route (/), the application’s home page:

app.post('/', function (req, res) {
 res.send('Got a POST request');
});

Respond to a PUT request to the /user route:

app.put('/user', function (req, res) {
 res.send('Got a PUT request at /user');
});

Respond to a DELETE request to the /user route:

app.delete('/user', function (req, res) {
 res.send('Got a DELETE request at /user');
});

For more details on Routing visit: http://expressjs.com/en/guide/routing.html

Middleware in Express
Middleware functions are functions that have access to the request object (req), the response object (res), and the next middleware function in the application’s request-response cycle. The next middleware function is commonly denoted by a variable named next.

Middleware functions can perform the following tasks:

  • Execute any code.
  • Make changes to the request and the response objects.
  • End the request-response cycle.
  • Call the next middleware in the stack.

If the current middleware function does not end the request-response cycle, it must call next() to pass control to the next middleware function. Otherwise, the request will be left hanging.

Here is a simple example of a middleware function called “sampleMiddleware”. This function just prints “Middleware Called” when a request to the app passes through it. The middleware function is assigned to a variable named sampleMiddleware.

var sampleMiddleware = function (req, res, next) {
 console.log(‘Middleware Called');
 next();
};

To load the middleware function, call app.use(), specifying the middleware function. For example, the following code loads the sampleMiddleware middleware function before the route to the root path (/).

var express = require('express');
var app = express();

var sampleMiddleware = function (req, res, next) {
 console.log(‘Middleware Called');
 next();

};

app.use(sampleMiddleware);

app.get('/', function (req, res) {
 res.send('Hello World!');
});

app.listen(3000);

very time the app receives a request, it prints the message “Middleware Called” to the terminal.

The order of middleware loading is important: middleware functions that are loaded first are also executed first.

If sampleMiddleware is loaded after the route to the root path, the request never reaches it and the app doesn’t print “Middleware Called”, because the route handler of the root path terminates the request-response cycle.

The middleware function sampleMiddleware simply prints a message, then passes on the request to the next middleware function in the stack by calling the next() function.

An Express application can use the following types of middleware:

  • Application-level middleware
  • Router-level middleware
  • Error-handling middleware
  • Built-in middleware
  • Third-party middleware

You can load application-level and router-level middleware with an optional mount path. You can also load a series of middleware functions together, which creates a sub-stack of the middleware system at a mount point.

You can find more information about middle ware at http://expressjs.com/en/guide/using-middleware.html

Working with Database
Here in this blog we will see how to connect with MongoDB and using mongoose.

I assume here that you are already aware of what is MongoDB and has installed mongoDB, if you haven’t installed mongoDB go to following link to install it on Linux https://docs.mongodb.org/manual/administration/install-on-linux/

Mongoose provides a straight-forward, schema-based solution to model your application data. It includes built-in type casting, validation, query building, business logic hooks and more, out of the box.

Installing Mongoose
Assuming you’ve already installed Node.js, create a directory to hold your application, and make that your working directory.

Now install Mongoose in the app directory and save it in the dependencies list. For example:

$ npm install mongoose –save

To install Mongoose temporarily and not add it to the dependencies list, omit the --save option:

$ npm install mongoose

Now lets see how to connect to MongoDB and interact with Mongoose.

Creating the connection with mongoDB

var mongoose=require('mongoose');
mongoose.connect('mongodb://localhost/blogs');

Defining the model definition for a collection that holds blog details

//Blog Model definition
var Blog=mongoose.model('Blog',{
 title:'String',
 content:'String',
 author:'String'
});

Finding all blogs

// Find all blogs
Blog.find({},function(err,blogs){
  if(err){
     res.status(500);
     res.send('');
  } else{
     res.status(200);
     res.send(blogs);
  }
});

Adding New Blog to collection

// Add New Blog
var blog=new Blog();
blog.title=req.body.title;
blog.content=req.body.content;
blog.author=req.body.author;
blog.save(function(err,blog){
 if(err){
 //handle error
 }else{
 //blog will have object for the newly created blog
 }
});

Finding single blog based on id

Blog.findOne({_id:1},function(err,blog){
 if(err){
 //When blog with id is not found
 }else{
 // blog found
 }
});

Deleting blog based on id

Blog.remove({_id:1},function(err,result){
 if(err){
 //When blog with id is not found
 }else{
 // blog removed
 }
});

NOTE: We will be seeing things kept together in Sample API

Working with Session
We will see express-session module for working with session in Express. “express-session” is a simple module that provides session tracking functionality in Express framework.

Installing express-session
Assuming you’ve already installed Node.js, create a directory to hold your application, and make that your working directory.

Now install express-session in the app directory and save it in the dependencies list. For example:

$ npm install express-session –save

To install express-session temporarily and not add it to the dependencies list, omit the --save option:

$ npm install express-session

Now as you have installed express-session, let just go ahead and start using it. A simple example using express-session to store page views for a user is given below, in this example, we are creating a sample express application which store the views count base on the URL called and severed by it.

In below code we have created session object by calling var session = require(express-session)

Then we have injected and used the session as express middleware with app.use(session….

with adding it as middle ware express req.session will get populated in the request parameter of the routes.

Then we have create one more middleware(countMiddleware) that counts the visit based on URL and stores it in req.session object.

var express = require('express')
var parseurl = require('parseurl')
var session = require('express-session')
var app = express()

app.use(session({
 secret: 'keyboard cat',
 resave: false,
 saveUninitialized: true
}))

var countMiddleware=function (req, res, next) {
 var views = req.session.views

 if (!views) {
 views = req.session.views = {}
 }

 // get the url pathname
 var pathname = parseurl(req).pathname

 // count the views
 views[pathname] = (views[pathname] || 0) + 1

 next()
}
app.use(countMiddleware)

app.get('/foo', function (req, res, next) {
 res.send('you viewed this page ' + req.session.views['/foo'] + ' times')
})

app.get('/bar', function (req, res, next) {
 res.send('you viewed this page ' + req.session.views['/bar'] + ' times')

});

You can find other options and more usage information about the express-session middleware at https://github.com/expressjs/session

In this example session are stored in the process memory so if you restart your app, all the sessions will get destroyed, if you need to make your session persistent you need to use Session Store compatible to “express-session”. We will see in more details in our Sample API which is the next section, about storing session in MongoDB.

Below are the list of well known Session Store compatible with “express-session”

  • cassandra-store An Apache Cassandra-based session store.
  • cluster-store A wrapper for using in-process / embedded stores – such as SQLite (via knex), leveldb, files, or memory – with node cluster (desirable for Raspberry Pi 2 and other multi-core embedded devices).
  • connect-couchbase A couchbase-based session store.
  • connect-mssql A SQL Server-based session store.
  • connect-monetdb A MonetDB-based session store.
  • connect-mongo A MongoDB-based session store.
  • connect-mongodb-session Lightweight MongoDB-based session store built and maintained by MongoDB.
  • connect-pg-simple A PostgreSQL-based session store.
  • connect-redis A Redis-based session store.
  • connect-session-knex A session store using Knex.js, which is a SQL query builder for PostgreSQL, MySQL, MariaDB, SQLite3, and Oracle.
  • connect-session-sequelize A session store using Sequelize.js, which is a Node.js / io.js ORM for PostgreSQL, MySQL, SQLite and MSSQL.
  • connect-sqlite3 A SQLite3 session store modeled after the TJ’s connect-redis store.
  • express-nedb-session A NeDB-based session store.
  • level-session-store A LevelDB-based session store.
  • mssql-session-store A SQL Server-based session store.
  • nedb-session-store An alternate NeDB-based (either in-memory or file-persisted) session store.
  • session-file-store A file system-based session store.
  • session-rethinkdb A RethinkDB-based session store.

Building Sample API
Here i am gone show you code snippets which gets a RESTFul API for creating blog, along with a dummy login check and session management. Basic goal of this sample api is to cover all the fundamentals we have seen above and implement it.

In the sample code below we have following main file to focus on

  • blog-app.js — This file act as a module which can be later used for unit testing, it contains the express application that do add session middle ware and handle mongo connection and expose “app” object of express application. It also contains endpoints that allows user to login and log out.
  • blog.js — This file contains the router and API paths that allows to following functionality, it also contains one middleware which checks for session and rejects the request with 401 status code if session is not valid.
    • Add Blog (POST /blogs)
    • Update Blog (PUT /blogs/:blogId)
    • Delete Blog (DELETE /blogs/:blogId)
    • Get Blog Details (GET /blogs/:blogId)
  • blog-server.js — This file make use of “blog-app” module and starts the server @ port 3000.
  • package.json — This file contain meta data about the application and dependency list.

So if you put this files together in a folder and execute following command then application will start running on port 3000, and you will be able to see it in browser at : http://localhost:3000

$ npm install
$ node blog-server.js

First command in above snippet will install all the dependencies and second command will start the server.

Make sure you have MongoDB running in your machine.

Code of blog-app.js

var express=require('express');
var app=express();
var mongoose=require('mongoose');
var bodyParser=require('body-parser');
var session=require('express-session');

var MongoStore=require('connect-mongo')(session);

//Init mongoose connection
mongoose.connect('mongodb://localhost/blogs');
var Blog=mongoose.model('Blog',{
 title:'String',
 content:'String',
 author:'String'
});
global.Blog=Blog;

//Add Body parser
app.use(bodyParser.json({type:'application/json'}));
app.use(session({
 secret:'tiest9383D829d',
 resave:true,
 saveUninitialized:true,
 store:new MongoStore({mongooseConnection:mongoose.connection})
}));

var blogController=require('./blog');
app.use('/blogs',blogController);

app.post('/login',function(req,res){
 if(req.body.username=='dipesh'){
   req.session.isLoggedIn = 'Y';
   req.session.user = 'Dipesh';
   res.send('Success');
 } else{
   res.status(500);
   res.end();
 }
});

app.get('/logout', function(req,res){
 req.session.destroy();
 res.status(205);
 res.end();
});

module.exports=app;

Code for blog.js

'use strict';
var express=require('express');
var router=express.Router();

router.use(function(req,res,next){
 if(req.session.isLoggedIn=='Y'){
   next();
 }else{
   res.status(401);
   res.end();
 }
});

router.get('/',function(req,res){
 Blog.find({},function(err,blogs){
 if(err){
   res.status(500);
   res.send('');
 }else{
   res.status(200);
   res.send(blogs);
 }
 });
});

router.post('/',function(req,res){
 var blog=new Blog();
 blog.title=req.body.title;
 blog.content=req.body.content;
 blog.author=req.body.author;

 blog.save(function(err,blog){
 if(err){
   res.status(500);
   res.send('');
 }else{
   res.status(201);
   res.send(blog);
 }
 })
});

router.get('/:blogId',function(req,res){
 Blog.findOne({_id:req.params.blogId},function(err,blog){
 if(err){
   res.status(404);
   res.send('');
 }else{
   res.status(200);
   res.send(blog);
 }
 });
});

router.put('/:blogId',function(req,res){
 Blog.findOne({_id:req.params.blogId},function(err,blog){
 if(err){
   res.status(404);
   res.send('');
 }else{
   blog.title=req.body.title;
   blog.content=req.body.content;
   blog.author=req.body.author;

 blog.save(function(err){
  if(err){
    res.status(500);
    res.send('');
  }else{
    res.status(204);
    res.send('');
  }
 })
 }
 });
});

router.delete('/:blogId',function(req,res){
 Blog.findOne({_id:req.params.blogId},function(err,blog){
  if(err){
     res.status(404);
     res.send('');
  } else{
     blog.remove(function(err){
       if(err){
          res.status(500);
          res.send('');
       }else{
          res.status(204);
          res.send('');
       }
    });
 }
 });
});

module.exports=router;

Code for blog-server.js

'use strict';
var app=require('./blog-app');
var server=app.listen(3000,function(err){
 if(err){
   console.log('Error starting blog server');
   console.log(err);
 } else{
   console.log(`Blog Server running on port ${server.address().port}`);
 }
});

Code for package.json

{
 "name": "sample-node-app",
 "version": "1.0.0",
 "description": "Sample Node Application",
 "main": "server.js",
 "scripts": {
 "test": "mocha blog-test.js"
 },
 "author": "Dipesh Patel",
 "license": "MIT",
 "dependencies": {
 "body-parser": "^1.14.2",
 "connect-mongo": "^1.1.0",
 "express": "^4.13.3",
 "express-session": "^1.12.1",
 "mongoose": "^4.3.4"
 },
 "devDependencies": {
 "mocha": "^2.3.4",
 "should": "^8.0.2",
 "supertest": "^1.1.0",
 "supertest-session": "^2.0.0"
 }
}

Unit Testing Code
Now here we will see how to Unit test one of our API from the above sample.

To do unit testing we need to following things

  • Assertion library
  • Test Framework
  • Test Runner
  • Express API executer, without running server

So for that we are going to use following NPM packages

Below file named “blog-test.js” is the file you need to put along with the files in above sample api.

Code for blog-test.js

'use strict';
var app=require('./blog-app');
var supertest=require('supertest-session');
var should=require('should');

describe('Testing Blog APIs',function(){
 it('Get blog details api should return error 401 if login does not exists',function(done){
  supertest(app)
   .get('/blogs/1')
   .expect(401)
   .end(function(err,response){
       response.status.should.be.equal(401);
       done();
  });
 });

 it('Get Blog Details api should return blog details if blog exists',function(done){
 var req=supertest(app);
 req
   .post('/login')
   .set('Content-Type','application/json')
   .send({username:'dipesh'})
   .expect(200)
   .end(function(err,response){
       response.status.should.be.equal(200);
       req
         .get('/blogs/568f845316099856f1cdfdf9')
         .expect(200)
         .end(function(err,response){
             response.status.should.be.equal(200);
             response.body.title.should.not.be.empty();
             done();
       });
   });
 });

 it('Get Blog details api should return error 404 if blog does not exists',function(done){
 var req=supertest(app);
 req
   .post('/login')
   .set('Content-Type','application/json')
   .send({username:'dipesh'})
   .expect(200)
   .end(function(err,response){
       response.status.should.be.equal(200);
       req
         .get('/blogs/1')
         .expect(404)
         .end(function(err,response){
             response.status.should.be.equal(404);
             done();
         });
     });
  })
});

Above file contains the test case for testing Blog APIs,

First in the above code we include the libraries that are required for testing, which are as below, here we created the instance of the ‘blog-app” module, supertest-session module and should module

var app=require('./blog-app');
var supertest=require('supertest-session');
var should=require('should');

Below statement is used to tell mocha that it contains the definition for the TestSuite, describe is used to define the test suites for the application, you can have more then one test suite in one test file also.

describe('Testing Blog APIs',function(){});

Each test case in the test suite is represented using “it” function, as below. Here we are creating one test case that checks for the status of the response and do the assertion on it. First argument in the “it” function is the name of the test case and second argument is the function having one parameter “done” which will be called when test case is completed executing. You can pass error object as first parameter of done function call to notify that test case has failed, and if you have written “throw” or any assertion is getting failed then that test case will be marked as failed at that point only and next test case in sequence will be executed.

it('Get blog details api should return error 401 if login does not exists',function(done){
  supertest(app)
    .get('/blogs/1')
    .expect(401)
    .end(function(err,response){
        response.status.should.be.equal(401);
        done();
     });
  });

Here in the above code we called supertest with the app object passed to it as parameter, then we call the api endpoint to call using “get” statement on line 3 in above code, then we say supertest to expect the return status as “401” and finally we end the request so request will be fired and response or error will be given in callback function.

In callback function we are checking the status of response to be 401, if that fails our test case will fail, else test case will pass.

To run the above test you need to run following command from your terminal inside your application folder

$ mocha blog-test.js

this command will start the test case execution and give following output in case of all test case getting passed.

Testing Blog APIs
✓ Get blog details api should return error 401 if login does not exists (44ms)
✓ Get Blog Details api should return blog details if blog exists (45ms)
✓ Get Blog details api should return error 404 if blog does not exists

3 passing (112ms)

Author

Dipesh Patel

Being a chief technology officer, most of my time goes into R&D of progressive technologies. Learning and experimenting on new technologies is my passion. And I live my passion by working on challenging and disruptive projects and products. Apart from being a geek, I love spending quality time with family and pets.

Want more stories like this?

Subscribe to receive our exclusive insights.
  • This field is for validation purposes and should be left unchanged.