~ read.

Using mongoDB with Node.js

In this little tutorial, we will cover how to access a mongoDB database from node.js. Compared to traditional relational database systems such as MySQL, mongoDB belongs to the so-called NoSQL movement of new databases which eschew having tables with strict schema and instead allow records to be inserted into a single "table" - or in mongoDB terminology documents of different structure can be inserted into a single collection. This opens a whole can of worms, and there are some drawbacks and points to consider before settling on a NoSQL data store. However, in this post I will not discuss this any further. Instead, we will look at how to setup a connection to a mongo database from node.js.

We start with a little server program, let's call it mongo_server.js. It begins pretty simple, with a few lines to include the necessary packages we are going to use:

var http = require("http"); 
var url = require("url");
var mongojs = require("mongojs");
var querystring = require('querystring');

The next thing we should define is the database we want to use:

var databaseUrl  = "learning_mongo";

Then we add the following 2 lines:

var collections = ["users", "blogs"]
var db = mongojs.connect(databaseUrl, collections) 

We have to explain this a little bit. As already said, from SQL we know the concept of tables. A collection might seem, at least at the first glance, to be something similar, but it is important to emphasize the difference:

A collection may be best understood as a collection of JavaScript objects that share a common name, but they do not have to have the same keys, but can differ - so it is on the user to ensure the integrity of the database and not to mix wildy different documents in one collection, which is very bad practice. Consider the example of a Blog. Individual blog posts are connected with comments.

In a relational database, we would create two tables, one for the blog posts and one for the associated comments, and then link them via foreign and private keys. While something like this is possible in mongo, we will store the comments instead in the blog object, i.e. we will create a blog post object that contains a list of embedded comments. That way, all the associated information on a blog post is already included in the object itself. On the down-side, in many instances such an approach might yield to data duplication and henceforth issues of ensuring data consistency.

Let us set up our server architecture:

var app    = http.createServer(function (request, response) 
  { 
      var queryData = ""; 
     if(request.method == 'GET') 
      {    

      };

   if(request.method == 'POST') {
               request.on('data', function(data) {
                   queryData += data;
               if(queryData.length > 1e6) {
                       queryData = "";
                   response.writeHead(413, {'Content-Type': 'text/plain'}).end();
                   request.connection.destroy();
               }
           });


        request.on('end', function() {

             response.post = querystring.parse(queryData);
         var data = response.post.data;   
       

    var pathname = url.parse(request.url).pathname;

    console.log("Path is " + pathname);

    switch(pathname)
      {
          case "/new_document":
        console.log("should store a new document");
      break;  
      }

      });  

         }

  });


io = require('socket.io').listen(app)
app.listen(8899)

It looks a bit different than before we wanted our server also to interact with socket io. That's the meaning of the last two lines:

io = require('socket.io').listen(app)
app.listen(8899)

But in principle our server looks like the one we have built for our SQLite database.

Everything is prepared now. We can store new documents now. On our client side a call might look like this:

new_document = function(doc)
  {
      var url = url_prefix + "/new_document";
  var data = JSON.stringify(doc);
      
  $.post(url,
    {
        'data' : data},
    function(data){
        
    console.log("SUCCESS");
      
    }).error(function(data, textStatus)
    {
        alert("PROBLEM");
    });
  
  }

As a param we we pass a doument. We assume that this is a valid Javascript oblject, like this:

doc = {};
doc.title = "Title"
doc.subtitle = "Subtitle"
doc.description = "Description"; 

Let's have a look how we will store it.

Since we call a POST request the POST section of our server should take take of it. We have learned that it is a bit tricky, since douments have a huge size – therefore will stile have this seemingly complicated post function we nevertheless may ignore.

The important part is this one:

Since we call a POST request the POST section of our server should take care of it. We have learned that it is a bit tricky, since douments have a huge size – therefore will still have this seemingly complicated post function we nevertheless may ignore.

The important part is this one:

 request.on('end', function() {
          response.post = querystring.parse(queryData);
      var data = response.post.data;   
      });  

Let's analyze it.
The first lines parses the querystring and adds it to the reponse. Since it is already passed as a JSON string there is no need for a cast. The data now holds the document that we have passed.

Since we might want to store a lot of differenct objects and document types it is a good idea to have a switch. That is what the pathname is for:

var pathname = url.parse(request.url).pathname; 

So our function might look like this:

request.on('end', function() {        
         response.post = querystring.parse(queryData);
     var data = response.post.data;    
   var pathname = url.parse(request.url).pathname;

    switch(pathname)
      {
          case "/new_document":
        if (db) new_document(data, response);
      break;  

      default:
       
      break;  
      }
      });   

Storage is easy then:

function new_document (data, response)
    {
         
   db.documents.save(data, function(err, savedDoc){
            
     response.writeHead(200, { 
             'Content-Type':  'application/json',
         'Access-Control-Allow-Origin' : '*'
     }); 
     
     var myJSONText = JSON.stringify("OK");
         response.end( myJSONText );  
     });
   
     }  

As you see the db.documents.save function is takes a callback: The database might need some time for storage.

More or less our database is still a black box. We are strongly inclined to know more, see how many documents we have already stored. So it's another good idea to write a function that gives us some stats. Like that:

function db_info ()
  {
      var url = url_prefix + "/db_info";
  
  $.ajax({
        type : "GET",
    url : url

    }).done(function(msg) (
    
        });
Server-side this function might look like this:
server_db_info = function(response)
  {
      db.stats( function(err, res)
    {    
    response.writeHead(200, { 
           'Content-Type':  'application/json',
       'Access-Control-Allow-Origin' : '*'
    }); 
           
       var myJSONText = JSON.stringify(res);
       response.end( myJSONText );
       
     });  

After having stored a few documents the next thing we probably would like to do is to read all the documents already stored. Nothing easier than that. On our client side we define a GET-function. Remember: We want to GET something from the database. Like this:

function all_docs ()
  {
      var url = url_prefix + "/all_docs";
  
  
  $.ajax({
        type : "GET",
    url : url

    }).done(function(msg) {
        
    var data = JSON.parse(msg);
    });

Have a look at the server code. The part which is interesting to us may be found in the GET section:

  if(request.method == 'GET') 
       {   
           var pathname = url.parse(request.url).pathname;    
       switch(pathname)
         {
             case '/db_info':
            if (db) db_info(response);
         break;         
         case '/all_docs':
            if (db) all_docs(response);
         break;        
         }           
       }

We parse the url and then we branch our respective call, using the pathname as the input of our switch function.

this.update_user = function(data, response)
  {
      var update_user = JSON.parse(data);
  var s = {name: update_user.name };
  
  db.users.find(s, function(err, user){  
   if (user.length > 0)
     {
         if (user[0].nick != update_user.nick) user[0].nick = update_user.nick;     
    console.log("ID " +  user[0]._id);
    db.users.update(
           { _id: user[0]._id },
       {
             $set: { 'nick': update_user.nick, 
              'mail' : update_user.mail,
              'password' : update_user.password, 
           },
       }
    )
     console.log("UPDATE - User found"); 
    }
    });