How To Write A Simple Node.js/MongoDB Web Service for an iOS App

Learn how to create a simple Node.js and MongoDB web service for an iOS app in this tutorial. By Michael Katz.

Leave a rating/review
Save for later
Share
You are currently viewing page 4 of 6 of this article. Click here to view the first page.

Introducing MongoDB

MongoDB is a database that stores JSON objects. Unlike a SQL database, a NoSQL database like Mongo does not support entity relationships. In addition, there is no pre-defined schema, so entities in the same collection (or “table”, in relational-speak) don’t need to have the same fields or conform to any predefined pattern.

MongoDB also provides a powerful querying language map-reduce along with support for location data. MongoDB is popular for its ability to scale, replicate and shard. Scaling and high-availability features are not covered in this tutorial.

The biggest drawbacks of MongoDB are the lack of relationship support and that it can be a memory hog as it memory-maps the actual database file. These issues can be mitigated by carefully structuring the data; this will be covered in part 2 of this tutorial.

Because of the close relationship between MongoDB documents and JSON, MondoDB is a great choice as a database for both web and mobile apps. MongoDB doesn’t store raw JSON; instead, the documents are in a format called BSON — Binary JSON — that is more efficient for data storage and queries. BSON also has the advantage of supporting more datatypes than JSON, such as dates and C-types.

Adding MongoDB to Your Project

MongoDB is a native application and is accessed through drivers. There are a number of drivers for almost any environment, including Node.js. The MongoDB driver connects to the MongoDB server and issues commands to update or read data.

This means you’ll need a running MongoDB instance listening on an open port. Fortunately, that’s your very next step! :]

Open a new instance of Terminal and execute the following commands:

cd /usr/local/opt/mongodb/; mongod

This starts the MongoDB server daemon.

Now the MongoDB service is up and running on the default port of 27017.

Although the MongoDB driver provides database connectivity, it still has to be wired up to the server to translate incoming HTTP requests into the proper database commands.

Creating a MongoDB Collection Driver

Remember the /:a/:b/:c route you implemented earlier? What if you could use that pattern instead to look up database entries?

Since MongoDB documents are organized into collections, the route could be something simple like: /:collection/:entity which lets you access objects based on a simple addressing system in an extremely RESTful fashion!

Kill your Node instance and execute the following commands in Terminal:

edit collectionDriver.js

Add the following code to collectionDriver.js:

var ObjectID = require('mongodb').ObjectID;

This line imports the various required packages; in this case, it’s the ObjectID function from the MongoDB package.

Note: If you’re familiar with traditional databases, you probably understand the term “primary key”. MongoDB has a similar concept: by default, new entities are assigned a unique _id field of datatype ObjectID that MongoDB uses for optimized lookup and insertion. Since ObjectID is a BSON type and not a JSON type, you’ll have to convert any incoming strings to ObjectIDs if they’re to be used when comparing against an “_id” field.

Add the following code to collectionDriver.js just after the line you added above:

CollectionDriver = function(db) {
  this.db = db;
};

This function defines the CollectionDriver constructor method; it stores a MongoDB client instance for later use. In JavaScript, this is a reference to the current context, just like self in Objective-C.

Still working in the same file, add the following code below the block you just added:

CollectionDriver.prototype.getCollection = function(collectionName, callback) {
  this.db.collection(collectionName, function(error, the_collection) {
    if( error ) callback(error);
    else callback(null, the_collection);
  });
};

This section defines a helper method getCollection to obtain a Mongo collection by name. You define class methods by adding functions to prototype.

db.collection(name,callback) fetches the collection object and returns the collection — or an error — to the callback.

Add the following code to collectionDriver.js, below the block you just added:

CollectionDriver.prototype.findAll = function(collectionName, callback) {
    this.getCollection(collectionName, function(error, the_collection) { //A
      if( error ) callback(error);
      else {
        the_collection.find().toArray(function(error, results) { //B
          if( error ) callback(error);
          else callback(null, results);
        });
      }
    });
};

CollectionDriver.prototype.findAll gets the collection in line A above, and if there is no error such as an inability to access the MongoDB server, it calls find() on it in line B above. This returns all of the found objects.

find() returns a data cursor that can be used to iterate over the matching objects. find() can also accept a selector object to filter the results. toArray() organizes all the results in an array and passes it to the callback. This final callback then returns to the caller with either an error or all of the found objects in the array.

Still working in the same file, add the following code below the block you just added:

CollectionDriver.prototype.get = function(collectionName, id, callback) { //A
    this.getCollection(collectionName, function(error, the_collection) {
        if (error) callback(error);
        else {
            var checkForHexRegExp = new RegExp("^[0-9a-fA-F]{24}$"); //B
            if (!checkForHexRegExp.test(id)) callback({error: "invalid id"});
            else the_collection.findOne({'_id':ObjectID(id)}, function(error,doc) { //C
                if (error) callback(error);
                else callback(null, doc);
            });
        }
    });
};

In line A above, CollectionDriver.prototype.get obtains a single item from a collection by its _id. Similar to prototype.findAll method, this call first obtains the collection object then performs a findOne against the returned object. Since this matches the _id field, a find(), or findOne() in this case, has to match it using the correct datatype.

MongoDB stores _id fields as BSON type ObjectID. In line C above, ObjectID() (C) takes a string and turns it into a BSON ObjectID to match against the collection. However, ObjectID() is persnickety and requires the appropriate hex string or it will return an error: hence, the regex check up front in line B.

This doesn’t guarantee there is a matching object with that _id, but it guarantees that ObjectID will be able to parse the string. The selector {'_id':ObjectID(id)} matches the _id field exactly against the supplied id.

Note: Reading from a non-existent collection or entity is not an error – the MongoDB driver just returns an empty container.

Add the following line to collectionDriver.js below the block you just added:

exports.CollectionDriver = CollectionDriver;

This line declares the exposed, or exported, entities to other applications that list collectionDriver.js as a required module.

Save your changes — you’re done with this file! Now all you need is a way to call this file.