Windows Azure DocumentDB in NodeJS app
I’ve read a few weeks ago a small article about DocumentDB in Windows Azure which is Microsoft’s NoSQL solution in cloud, but I didn’t have time to go more in depth, until today! I came across this wonderful tutorial which explains in detail how to develop a small CRUD application in NodeJS which stores the information in DocumentDB.
In short:
- Assuming that you have an azure account, you are logging in to https://portal.azure.com/ and you are creating a DocumentDB database by clicking the big + button at the bottom
- If your favorite IDE is Visual Studio like me then install NodeJs Tools from here
- Add nconf and documentdb modules through npm. Add config.json file with your database details
- Replace code in routes/index.js with this:
// import the modules we will use var DocumentDBClient = require('documentdb').DocumentClient; var nconf = require('nconf'); // tell nconf which config file to use nconf.env(); nconf.file({ file: 'config.json' }); var host = nconf.get("HOST"); var authKey = nconf.get("AUTH_KEY"); var databaseId = nconf.get("DATABASE"); var collectionId = nconf.get("COLLECTION"); // create some global variables which we will use later to hold instances of the DocumentDBClient, Database and Collection // create an instance of the DocumentDB client var client = new DocumentDBClient(host, { masterKey: authKey }); exports.index = function (req, res) { // before we can query for Items in the document store, we need to ensure we // have a database with a collection then use the collection to read the documents readOrCreateDatabase(function (database) { readOrCreateCollection(database, function (collection) { listItems(collection, function (items) { res.render('index', { title: 'My ToDo List', tasks: items }); }); }); }); }; exports.createOrUpdateItem = function (req, res) { //first have to set the database & collection context so that we have the self links readOrCreateDatabase(function (database) { readOrCreateCollection(database, function (collection) { //if we find an item on the form, we'll create it in the database var item = req.body.item; if (item) { createItem(collection, item, function () { res.redirect('/'); }); //else let's look for items marked as completed, //and update that item in the database } else { var completed = req.body.completed; //check if completed is actually an Array, if not make it one. //this happens when you select only one item if (!completed.forEach) completed = [completed]; //use a recursive function to loop through items in //array calling updateItem each time through function updater(i) { if (i < completed.length) { updateItem(collection, completed[i], function () { updater(i + 1); }); } else { res.redirect('/'); } } //kick off the recursion updater(0); } }); }); } // update item var updateItem = function (collection, itemId, callback) { //first fetch the document based on the id getItem(collection, itemId, function (doc) { //now replace the document with the updated one doc.completed = true; client.replaceDocument(doc._self, doc, function (err, replacedDoc) { if (err) { throw (err); } callback(); }); }); } // get item var getItem = function (collection, itemId, callback) { client.queryDocuments(collection._self, 'SELECT * FROM root r WHERE r.id="' + itemId + '"').toArray(function (err, results) { if (err) { throw (err); } callback(results[0]); }); } // create new item var createItem = function (collection, documentDefinition, callback) { documentDefinition.completed = false; client.createDocument(collection._self, documentDefinition, function (err, doc) { if (err) { throw (err); } callback(); }); } // query the provided collection for all non-complete items var listItems = function (collection, callback) { client.queryDocuments(collection._self, 'SELECT * FROM root r WHERE r.completed=false').toArray(function (err, docs) { if (err) { throw (err); } callback(docs); }); } // if the database does not exist, then create it, else return the database object var readOrCreateDatabase = function (callback) { client.queryDatabases('SELECT * FROM root r WHERE r.id="' + databaseId + '"').toArray(function (err, results) { if (err) { // some error occured, rethrow up throw (err); } if (!err && results.length === 0) { // no error occured, but there were no results returned // indicating no database exists matching the query client.createDatabase({ id: databaseId }, function (err, createdDatabase) { callback(createdDatabase); }); } else { // we found a database callback(results[0]); } }); }; // if the collection does not exist for the database provided, create it, else return the collection object var readOrCreateCollection = function (database, callback) { client.queryCollections(database._self, 'SELECT * FROM root r WHERE r.id="' + collectionId + '"').toArray(function (err, results) { if (err) { // some error occured, rethrow up throw (err); } if (!err && results.length === 0) { // no error occured, but there were no results returned //indicating no collection exists in the provided database matching the query client.createCollection(database._self, { id: collectionId }, function (err, createdCollection) { callback(createdCollection); }); } else { // we found a collection callback(results[0]); } }); };
5. App.js file should look like this
/** * Module dependencies. */ var express = require('express'); var routes = require('./routes'); var user = require('./routes/user'); var http = require('http'); var path = require('path'); var app = express(); // all environments app.set('port', process.env.PORT || 3000); app.set('views', path.join(__dirname, 'views')); app.set('view engine', 'jade'); app.use(express.favicon()); app.use(express.logger('dev')); app.use(express.json()); app.use(express.urlencoded()); app.use(express.methodOverride()); app.use(app.router); app.use(require('stylus').middleware(path.join(__dirname, 'public'))); app.use(express.static(path.join(__dirname, 'public'))); // development only if ('development' == app.get('env')) { app.use(express.errorHandler()); } app.get('/', routes.index); app.post('/', routes.createOrUpdateItem); http.createServer(app).listen(app.get('port'), function () { console.log('Express server listening on port ' + app.get('port')); });
6. Then create your form in jade
Result should be something like this: