Schemas in MongoDB with Node.js

Note: This article is for people who are somewhat new to MongoDB and Node.js, but have a decent knowledge of JavaScript.

Although document storage allows for schema-free databases, in practice this can create problems. If a programmer or database administrator designs a schema, there’s nothing stopping another programmer on the team from dropping a document into a collection with a completely different structure from the rest of the documents. This can result in bugs in the system.

Many language drivers include libraries (either native or third party) that help you create schemas. Let’s take a look at one NoSQL database in particular, MongoDB, and a server-side system called Node.js (which uses JavaScript), and work through an example to see how it’s done.

The library I’m using is called Mongoose, which works alongside the native Mongo driver to add schema capabilities. For Node.js, I’m using the popular Express framework. (I don’t have room for a full introduction to Express; if you’re new to it and want to get up to speed, check out this link. Also make sure you’re familiar with how to add packages with the npm package manager.

To get started, I created a new project for Express using the express command, and used npm to install the required libraries (as described at the end of the express command output). Then I used npm to add the mongoose library.

Now let’s look at the actual code. I usually use either Eclipse or Cloud9 IDE. Because a lot of people new to Node.js like to use Cloud9, that’s what I’m using for this project.

The next step is to create a JavaScript source file that contains the schema for our database. To keep this simple, I’m using a basic schema for a contact, including name, title, company, and phone number. Here’s the basic format:

{

“_id” : ObjectId(“4feb2b519a265fcf553a0173″),

“name” : “George Washington”,

“title” : “President”,

“company” : “Government”,

“phones” : [

{

“work” : “202-555-1111″

},

{

“cell” : “800-555-1212″

}

]

}

I’ll put my code file inside a directory called models, which is inside my project’s root directory. Here’s the source file, which I’m calling schema.js:

var mongoose = require(‘mongoose’), Schema = mongoose.Schema;

// Connect mongoose to the database

mongoose.connect(‘mongodb://localhost/contacts’);

// Create the model

var PhonesSchema = new Schema({ type: String, number: String });

var ContactSchema = new Schema({ name: String, title: String,

company: String, phones: [ PhonesSchema ] });

// Register the model with mongoose

var Contact = mongoose.model(‘Names’, ContactSchema);

// Get an item by ID

exports.getItems = function(handlers) {

return Contact.find(function(err, list) {

if (!err) {

handlers.success(list);

} else {

handlers.error(err);

}

});

};

exports.addItem = function(handlers) {

var doc = new Contact();

doc.name = “Thomas Jefferson”;

doc.title = “Vice President”;

doc.company = “Government”;

// doc.test = ‘something'; // uncomment to try out schema

doc.phones = [ { work: ‘800-555-1234′}, { home: ‘888-555-5432′} ]

return doc.save(function(err, data) {

if (!err) {

handlers.success(data);

} else {

handlers.error(err);

}

});

};

I won’t walk you all through the source code, since you’re already familiar with JavaScript. But I’ll mention a few things. First, the line at the top imports the mongoose module. In Node.js, modules are imported through a requires statement.

Next, you can see where I wrote the schema. I split it up into two separate schemas, one for a single phone item, and then one for a name contact, which includes an array of phones.

In the first member function, getItems, I’m calling Contact.find to get back all the documents. And notice I’m passing in an object containing my callback functions. Similarly with the next function, addItem, which inserts some hard-coded data.

Now here’s the code for the main app. I’m only showing the parts I modified from the starter app.js created by express. At the top, I added a require statement for my schema code:

var express = require(‘express’) , routes = require(‘./routes’)

, schema = require(‘./models/schema.js’);

The require statement for my model returns me an object containing my two functions, getItems and addItem. Next, I added two routes, one the calls getItems, and one that calls addItem. I put these after the existing index route:

app.get(‘/list’, function(req, res){

schema.getItems({

success: function(data) {

// console.log(‘1′);

res.send(data);

},

error: function(err) {

res.send(err);

}

});

// console.log(‘2′);

});

app.get(‘/insert’, function(req, res) {

schema.addItem({

success: function(data) {

res.send(data);

},

error: function(err) {

res.send(err);

}

});

});

Notice how the callbacks work as functions in my object, one for success and one for error. The code in my schema then calls the appropriate function. Also notice that even though it’s a callback, the timing and chaining works appropriately.

If you uncomment the two console.log lines, you may well see in the console the ‘2’ appear before the ‘1.’ That’s okay; thanks to closure, the objects will still exist long enough for the callback to use them even though the outer function ran out. This is important when dealing with a database on a remote server, which might take a moment to retrieve the data.

What’s Next?

Although the main theme of this article was creating schema-based documents in MongoDB, you might want to take this example to the next step, and begin insert custom data and edit data. The Mongoose library includes functionality for all the usual database operations. As for the GUI, you certainly noticed I did very little with it here: just returning strings and JSON to the browser, and only using GET for everything. You’ll probably want to use a client JavaScript framework; I usually prefer jQuery, although there are others. You could include a grid and quickly get a contacts page up and running that uses a well-defined contacts schema.

 

Image: Alberto Zornetta/Shutterstock.com

Post a Comment

Your email address will not be published.