Saturday, May 28, 2016

NodeJS-Mongodb Persistent store

Multi-Tier applications

When we build web applications one of the most essential piece is services. Gone are the days we used to build single tier applications. We now have at a minimum 3-tier's to build on applications.

  • Tier-1 is UI
  • Tier-2 is services
  • Tier-3 is DB

What we will do in coming days

Real-time applications have a lot more. There will be at least 6-tiers which includes security, availability and performance tiers. For our case, we don't need that. But later we will see 



  • How to improve speed with NGINX on NodeJS solutions (performance) 
  • How to implement caching solutions with Reddis (Performance) 
  • How to use RabbitMQ with NodeJS (security & scalability) 
  • How to do authentication with NodeJS (security) 


3-Tier(s) are not perceptible logical layers, but in most occasions physical too. Running in different physical locations, systems, servers makes the application more manageable & testable.

So far in NodeJS samples we built, none of them help us persist data permanently. Our objective now is to connect NodeJS with MongoDB in the process persist data.

Install MongoDB

If you have not installed MongoDB, here's how you can do it
http://techkrishnan.blogspot.com/2016/05/installing-mongodb-osx.html

Install needed packages

We start in a less modular fashion to be conspicuous. As we expand we will have introduce other design patterns.

In addition to installing 'mongodb', I also installed the following 'querystring' and 'mongodb' client from npm

npm init
npm install querystring --save
npm install mongodb --save

Let's get started

Code to connect to MongoDB is below. Lets take a moment to dissect & understand what we got.

We connect to MongoDB using 'mongodb' package. It is through this client utility we connect to data store. We then define URI to connect to datastore. Connect method takes URI and callback as parameters. Always check for 'error' in callback, if error is NULL we return, else we proceed with the returned object - DB handle.

DB handle gives us baton to query for collection "contacts". MongoDB is a document DB, we do not have schema defined on it. Simple JSON object defined can be added into collection. Every documents in collection can be completely different. It is important on each step we check to see if we have right handle or received error. If it is an error, we return.

We have got code for OPTIONS method, we will talk about it in coming sessions

With the collection handle, next step is to check for GET and request path. If the path is what we are looking for we list the contents. MongoDB as asserted earlier is a document DB, which stores JSON collections. For us to interact over web, we need to convert this JSON objects into text (we could alternatively have 'content-type' set to 'application/javascript') for the sake of simplicity. Invoke find method on collection. If we receive error, we stop, else we return list of objects.

We move towards our next objective to insert data. We check to see if request method is POST and url is for 'contacts'. POST method passes data in BODY. We need to tune into HTTP events to access data. Once we have data, we convert it to 'utf-8' type string for processing, remmeber we sent data in RAW format from POSTMAN. Convert the string data received into JSON. If there are issues in conversion process, application may crash. To avoid such fallibilities we encompass conversion in try-catch block. We now have data that can be inserted into collection. This is done by invoking insert method. Again check with callback method for errors, if not, return the object which conveys how many objects were inserted.

How do we test this?

I suggest you install 'PostMan REST client' from Chrome/Firefox/Safari extension.

List contents


  • Open POSTMAN, 
  • choose GET method and set URL to be 'http://localhost:3000/contacts'. 
  • Tap on "SEND" method. 
  • If 'contacts' collection has data, we will see all of them . If there is no data, nothing comes back. 

Add/Insert content


  • We test POST method. Set method to 'POST', URL to be 'http://localhost:3000/contacts'
  • To pass data, select 'BODY' and ensure 'raw' option is selected with JSON (application/json) format. Add following data 
  • {"firstName":"someName1","lastName":"someName2","emailId":"someName@outlook.com","phoneNo":"XXX-XXX-3244"}
  • Tap on send. 


Now go back to GET, you should see inserted document(s).

/**
 * Created by krishnansriramrama on 5/27/16.
 */
var http = require('http');
var util = require('util');
var querystring = require('querystring');
var client = require('mongodb').MongoClient;

var uri = process.env.MONGOLAB_URI || 'mongodb://@127.0.0.1:27017/krishnan';

client.connect(uri, function(error, db) {
    if(error) {
        return console.error(error);
    }

    var collection = db.collection('contacts');
    var app = http.createServer(function(request, response) {
        var origin = (request.headers.origin || '*');
        if(request.method === 'OPTIONS') {
            response.writeHead('204', 'No Content', {
                'Access-Control-Allow-Origin': origin,
                'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
                'Access-Control-Allow-Headers': 'content-type, accept',
                'Access-Control-Max-Age': 10,
                'Content-Length': 0
            });
            response.end();
        } else if(request.method === 'GET' && (request.url === '/contacts' || request.url === '/contacts/')) {
            collection.find().toArray(function(error, results) {
                if(error) {
                    return console.error(error);
                }
                var body = JSON.stringify(results);
                response.writeHead('200', {
                    'Access-Control-Allow-Origin': origin,
                    'Content-Type': 'text/plain',
                    'Content-Length': body.length
                });
                console.log('List of objects returned from DB');
                console.dir(results);
                response.end(body);
            });
        } else if(request.method === 'POST' && (request.url === '/contacts' || request.url === '/contacts/')) {
            request.on('data', function(data) {
               console.log('Received Data');
                data = data.toString('utf-8');
                console.log(data);
                try {
                    data = JSON.parse(data);
                } catch(error) {
                    if(error) {
                        return console.error(error);
                    }
                }
                collection.insert(data, {safe:true}, function(error, obj) {
                   if(error) {
                       return console.error(error);
                   }
                    console.log('Object is saved');
                    console.log(JSON.stringify(obj));
                    var body = JSON.stringify(obj);
                    response.writeHead(200, {
                        'Access-Control-Allow-Origin': origin,
                        'Content-Type': 'text/plain',
                        'Content-Length': body.length
                    });
                    response.end(body);
                });
            });
        } else {
            response.end('Supported endpoints: GET /contacts, POST /contacts');
        }
    });
    var port = process.env.PORT || 3000;
    app.listen(port, function() {
        console.log('Server running in port - ' + port);
    });
});

No comments:

Post a Comment