Build a Node.js web application using Azure Cosmos DB

This Node.js tutorial shows you how to use Azure Cosmos DB and the DocumentDB API to store and access data from a Node.js Express application hosted on Azure Websites. You build a simple web-based task-management application, a ToDo app, that allows creating, retrieving, and completing tasks. The tasks are stored as JSON documents in Azure Cosmos DB. This tutorial walks you through the creation and deployment of the app and explains what's happening in each snippet.

Screen shot of the My Todo List application created in this Node.js tutorial

Don't have time to complete the tutorial and just want to get the complete solution? Not a problem, you can get the complete sample solution from GitHub. Just read the Readme file for instructions on how to run the app.

Prerequisites

Tip

This Node.js tutorial assumes that you have some prior experience using Node.js and Azure Websites.

Before following the instructions in this article, you should ensure that you have the following:

Step 1: Create an Azure Cosmos DB database account

Let's start by creating an Azure Cosmos DB account. If you already have an account or if you are using the Azure Cosmos DB Emulator for this tutorial, you can skip to Step 2: Create a new Node.js application.

  1. In a new window, sign in to the Azure portal.
  2. In the left pane, click New, click Databases, and then under Azure Cosmos DB, click Create.

    The Azure portal Databases pane

  3. On the New account blade, specify the configuration that you want for this Azure Cosmos DB account.

    With Azure Cosmos DB, you can choose one of four programming models: Gremlin (graph), MongoDB, SQL (DocumentDB), and Table (key-value), each which currently require a separate account.

    In this quick-start article we program against the DocumentDB API, so choose SQL (DocumentDB) as you fill out the form. If you have graph data for a social media app, or key/value (table) data, or data migrated from a MongoDB app, realize that Azure Cosmos DB can provide a highly available, globally distributed database service platform for all your mission-critical applications.

    Complete the fields on the New account blade, using the information in the following screenshot as a guide- your values may be different than the values in the screenshot.

    The new account blade for Azure Cosmos DB

    Setting Suggested value Description
    ID Unique value A unique name that identifies this Azure Cosmos DB account. Because documents.azure.com is appended to the ID that you provide to create your URI, use a unique but identifiable ID. The ID can contain only lowercase letters, numbers, and the hyphen (-) character, and it must contain 3 to 50 characters.
    API SQL (DocumentDB) We program against the DocumentDB API later in this article.
    Subscription Your subscription The Azure subscription that you want to use for this Azure Cosmos DB account.
    Resource Group The same value as ID The new resource-group name for your account. For simplicity, you can use the same name as your ID.
    Location The region closest to your users The geographic location in which to host your Azure Cosmos DB account. Choose the location that's closest to your users to give them the fastest access to the data.
  4. Click Create to create the account.
  5. On the top toolbar, click the Notifications icon The notification icon to monitor the deployment process.

    The Azure portal Notifications pane

  6. When the Notifications window indicates the deployment succeeded, close the notification window and open the new account from the All Resources tile on the Dashboard.

    The Azure Cosmos DB account on the All Resources tile

Now navigate to the Cosmos DB account blade, and click Keys, as we use these values in the web application we create next.

Screen shot of the Azure portal, showing a Cosmos DB account, with the Keys button highlighted on the Cosmos DB account blade, and the URI, PRIMARY KEY, and SECONDARY KEY values highlighted on the Keys blade

Step 2: Create a new Node.js application

Now let's learn to create a basic Hello World Node.js project using the Express framework.

  1. Open your favorite terminal, such as the Node.js command prompt.
  2. Navigate to the directory in which you'd like to store the new application.
  3. Use the express generator to generate a new application called todo.

     express todo
    
  4. Open your new todo directory and install dependencies.

     cd todo
     npm install
    
  5. Run your new application.

     npm start
    
  6. You can view your new application by navigating your browser to http://localhost:3000.

    Learn Node.js - Screenshot of the Hello World application in a browser window

    Then, to stop the application, press CTRL+C in the terminal window and then click y to terminate the batch job.

Step 3: Install additional modules

The package.json file is one of the files created in the root of the project. This file contains a list of additional modules that are required for your Node.js application. Later, when you deploy this application to Azure Websites, this file is used to determine which modules need to be installed on Azure to support your application. We still need to install two more packages for this tutorial.

  1. Back in the terminal, install the async module via npm.

     npm install async --save
    
  2. Install the documentdb module via npm. This is the module where all the Azure Cosmos DB magic happens.

     npm install documentdb --save
    
  3. A quick check of the package.json file of the application should show the additional modules. This file will tell Azure which packages to download and install when running your application. It should resemble the example below.

     {
       "name": "todo",
       "version": "0.0.0",
       "private": true,
       "scripts": {
         "start": "node ./bin/www"
       },
       "dependencies": {
         "async": "^2.1.4",
         "body-parser": "~1.15.2",
         "cookie-parser": "~1.4.3",
         "debug": "~2.2.0",
         "documentdb": "^1.10.0",
         "express": "~4.14.0",
         "jade": "~1.11.0",
         "morgan": "~1.7.0",
         "serve-favicon": "~2.3.0"
       }
     }
    

    This tells Node (and Azure later) that your application depends on these additional modules.

Step 4: Using the Azure Cosmos DB service in a node application

That takes care of all the initial setup and configuration, now let’s get down to why we’re here, and that’s to write some code using Azure Cosmos DB.

Create the model

  1. In the project directory, create a new directory named models in the same directory as the package.json file.
  2. In the models directory, create a new file named taskDao.js. This file will contain the model for the tasks created by our application.
  3. In the same models directory, create another new file named docdbUtils.js. This file will contain some useful, reusable, code that we will use throughout our application.
  4. Copy the following code in to docdbUtils.js

     var DocumentDBClient = require('documentdb').DocumentClient;
    
     var DocDBUtils = {
         getOrCreateDatabase: function (client, databaseId, callback) {
             var querySpec = {
                 query: 'SELECT * FROM root r WHERE r.id= @id',
                 parameters: [{
                     name: '@id',
                     value: databaseId
                 }]
             };
    
             client.queryDatabases(querySpec).toArray(function (err, results) {
                 if (err) {
                     callback(err);
    
                 } else {
                     if (results.length === 0) {
                         var databaseSpec = {
                             id: databaseId
                         };
    
                         client.createDatabase(databaseSpec, function (err, created) {
                             callback(null, created);
                         });
    
                     } else {
                         callback(null, results[0]);
                     }
                 }
             });
         },
    
         getOrCreateCollection: function (client, databaseLink, collectionId, callback) {
             var querySpec = {
                 query: 'SELECT * FROM root r WHERE r.id=@id',
                 parameters: [{
                     name: '@id',
                     value: collectionId
                 }]
             };               
    
             client.queryCollections(databaseLink, querySpec).toArray(function (err, results) {
                 if (err) {
                     callback(err);
    
                 } else {        
                     if (results.length === 0) {
                         var collectionSpec = {
                             id: collectionId
                         };
    
                         client.createCollection(databaseLink, collectionSpec, function (err, created) {
                             callback(null, created);
                         });
    
                     } else {
                         callback(null, results[0]);
                     }
                 }
             });
         }
     };
    
     module.exports = DocDBUtils;
    
  5. Save and close the docdbUtils.js file.

  6. At the beginning of the taskDao.js file, add the following code to reference the DocumentDBClient and the docdbUtils.js we created above:

     var DocumentDBClient = require('documentdb').DocumentClient;
     var docdbUtils = require('./docdbUtils');
    
  7. Next, you will add code to define and export the Task object. This is responsible for initializing our Task object and setting up the Database and Document Collection we will use.

     function TaskDao(documentDBClient, databaseId, collectionId) {
       this.client = documentDBClient;
       this.databaseId = databaseId;
       this.collectionId = collectionId;
    
       this.database = null;
       this.collection = null;
     }
    
     module.exports = TaskDao;
    
  8. Next, add the following code to define additional methods on the Task object, which allow interactions with data stored in Azure Cosmos DB.

     TaskDao.prototype = {
         init: function (callback) {
             var self = this;
    
             docdbUtils.getOrCreateDatabase(self.client, self.databaseId, function (err, db) {
                 if (err) {
                     callback(err);
                 } else {
                     self.database = db;
                     docdbUtils.getOrCreateCollection(self.client, self.database._self, self.collectionId, function (err, coll) {
                         if (err) {
                             callback(err);
    
                         } else {
                             self.collection = coll;
                         }
                     });
                 }
             });
         },
    
         find: function (querySpec, callback) {
             var self = this;
    
             self.client.queryDocuments(self.collection._self, querySpec).toArray(function (err, results) {
                 if (err) {
                     callback(err);
    
                 } else {
                     callback(null, results);
                 }
             });
         },
    
         addItem: function (item, callback) {
             var self = this;
    
             item.date = Date.now();
             item.completed = false;
    
             self.client.createDocument(self.collection._self, item, function (err, doc) {
                 if (err) {
                     callback(err);
    
                 } else {
                     callback(null, doc);
                 }
             });
         },
    
         updateItem: function (itemId, callback) {
             var self = this;
    
             self.getItem(itemId, function (err, doc) {
                 if (err) {
                     callback(err);
    
                 } else {
                     doc.completed = true;
    
                     self.client.replaceDocument(doc._self, doc, function (err, replaced) {
                         if (err) {
                             callback(err);
    
                         } else {
                             callback(null, replaced);
                         }
                     });
                 }
             });
         },
    
         getItem: function (itemId, callback) {
             var self = this;
    
             var querySpec = {
                 query: 'SELECT * FROM root r WHERE r.id = @id',
                 parameters: [{
                     name: '@id',
                     value: itemId
                 }]
             };
    
             self.client.queryDocuments(self.collection._self, querySpec).toArray(function (err, results) {
                 if (err) {
                     callback(err);
    
                 } else {
                     callback(null, results[0]);
                 }
             });
         }
     };
    
  9. Save and close the taskDao.js file.

Create the controller

  1. In the routes directory of your project, create a new file named tasklist.js.
  2. Add the following code to tasklist.js. This loads the DocumentDBClient and async modules, which are used by tasklist.js. This also defined the TaskList function, which is passed an instance of the Task object we defined earlier:

     var DocumentDBClient = require('documentdb').DocumentClient;
     var async = require('async');
    
     function TaskList(taskDao) {
       this.taskDao = taskDao;
     }
    
     module.exports = TaskList;
    
  3. Continue adding to the tasklist.js file by adding the methods used to showTasks, addTask, and completeTasks:

     TaskList.prototype = {
         showTasks: function (req, res) {
             var self = this;
    
             var querySpec = {
                 query: 'SELECT * FROM root r WHERE r.completed=@completed',
                 parameters: [{
                     name: '@completed',
                     value: false
                 }]
             };
    
             self.taskDao.find(querySpec, function (err, items) {
                 if (err) {
                     throw (err);
                 }
    
                 res.render('index', {
                     title: 'My ToDo List ',
                     tasks: items
                 });
             });
         },
    
         addTask: function (req, res) {
             var self = this;
             var item = req.body;
    
             self.taskDao.addItem(item, function (err) {
                 if (err) {
                     throw (err);
                 }
    
                 res.redirect('/');
             });
         },
    
         completeTask: function (req, res) {
             var self = this;
             var completedTasks = Object.keys(req.body);
    
             async.forEach(completedTasks, function taskIterator(completedTask, callback) {
                 self.taskDao.updateItem(completedTask, function (err) {
                     if (err) {
                         callback(err);
                     } else {
                         callback(null);
                     }
                 });
             }, function goHome(err) {
                 if (err) {
                     throw err;
                 } else {
                     res.redirect('/');
                 }
             });
         }
     };
    
  4. Save and close the tasklist.js file.

Add config.js

  1. In your project directory create a new file named config.js.
  2. Add the following to config.js. This defines configuration settings and values needed for our application.

     var config = {}
    
     config.host = process.env.HOST || "[the URI value from the Azure Cosmos DB Keys blade on http://portal.azure.com]";
     config.authKey = process.env.AUTH_KEY || "[the PRIMARY KEY value from the Azure Cosmos DB Keys blade on http://portal.azure.com]";
     config.databaseId = "ToDoList";
     config.collectionId = "Items";
    
     module.exports = config;
    
  3. In the config.js file, update the values of HOST and AUTH_KEY using the values found in the Keys blade of your Azure Cosmos DB account on the Microsoft Azure portal.
  4. Save and close the config.js file.

Modify app.js

  1. In the project directory, open the app.js file. This file was created earlier when the Express web application was created.
  2. Add the following code to the top of app.js

     var DocumentDBClient = require('documentdb').DocumentClient;
     var config = require('./config');
     var TaskList = require('./routes/tasklist');
     var TaskDao = require('./models/taskDao');
    
  3. This code defines the config file to be used, and proceeds to read values out of this file into some variables we will use soon.
  4. Replace the following two lines in app.js file:

     app.use('/', index);
     app.use('/users', users); 
    

    with the following snippet:

     var docDbClient = new DocumentDBClient(config.host, {
         masterKey: config.authKey
     });
     var taskDao = new TaskDao(docDbClient, config.databaseId, config.collectionId);
     var taskList = new TaskList(taskDao);
     taskDao.init();
    
     app.get('/', taskList.showTasks.bind(taskList));
     app.post('/addtask', taskList.addTask.bind(taskList));
     app.post('/completetask', taskList.completeTask.bind(taskList));
     app.set('view engine', 'jade');
    
  5. These lines define a new instance of our TaskDao object, with a new connection to Azure Cosmos DB (using the values read from the config.js), initialize the task object and then bind form actions to methods on our TaskList controller.
  6. Finally, save and close the app.js file, we're just about done.

Step 5: Build a user interface

Now let’s turn our attention to building the user interface so a user can actually interact with our application. The Express application we created uses Jade as the view engine. For more information on Jade please refer to http://jade-lang.com/.

  1. The layout.jade file in the views directory is used as a global template for other .jade files. In this step you will modify it to use Twitter Bootstrap, which is a toolkit that makes it easy to design a nice looking website.
  2. Open the layout.jade file found in the views folder and replace the contents with the following:

    doctype html
    html
      head
        title= title
        link(rel='stylesheet', href='//ajax.aspnetcdn.com/ajax/bootstrap/3.3.2/css/bootstrap.min.css')
        link(rel='stylesheet', href='/stylesheets/style.css')
      body
        nav.navbar.navbar-inverse.navbar-fixed-top
          div.navbar-header
            a.navbar-brand(href='#') My Tasks
        block content
        script(src='//ajax.aspnetcdn.com/ajax/jQuery/jquery-1.11.2.min.js')
        script(src='//ajax.aspnetcdn.com/ajax/bootstrap/3.3.2/bootstrap.min.js')
    

    This effectively tells the Jade engine to render some HTML for our application and creates a block called content where we can supply the layout for our content pages.

    Save and close this layout.jade file.

  3. Now open the index.jade file, the view that will be used by our application, and replace the content of the file with the following:

     extends layout
     block content
        h1 #{title}
        br
    
        form(action="/completetask", method="post")
          table.table.table-striped.table-bordered
            tr
              td Name
              td Category
              td Date
              td Complete
            if (typeof tasks === "undefined")
              tr
                td
            else
              each task in tasks
                tr
                  td #{task.name}
                  td #{task.category}
                  - var date  = new Date(task.date);
                  - var day   = date.getDate();
                  - var month = date.getMonth() + 1;
                  - var year  = date.getFullYear();
                  td #{month + "/" + day + "/" + year}
                  td
                    input(type="checkbox", name="#{task.id}", value="#{!task.completed}", checked=task.completed)
          button.btn.btn-primary(type="submit") Update tasks
        hr
        form.well(action="/addtask", method="post")
          .form-group
            label(for="name") Item Name:
            input.form-control(name="name", type="textbox")
          .form-group
            label(for="category") Item Category:
            input.form-control(name="category", type="textbox")
          br
          button.btn(type="submit") Add item
    

This extends layout, and provides content for the content placeholder we saw in the layout.jade file earlier.

In this layout we created two HTML forms.

The first form contains a table for our data and a button that allows us to update items by posting to /completetask method of our controller.

The second form contains two input fields and a button that allows us to create a new item by posting to /addtask method of our controller.

This should be all that we need for our application to work.

Step 6: Run your application locally

  1. To test the application on your local machine, run npm start in the terminal to start your application, then refresh your http://localhost:3000 browser page. The page should now look like the image below:

    Screenshot of the MyTodo List application in a browser window

    Tip

    If you receive an error about the indent in the layout.jade file or the index.jade file, ensure that the first two lines in both files is left justified, with no spaces. If there are spaces before the first two lines, remove them, save both files, then refresh your browser window.

  2. Use the Item, Item Name and Category fields to enter a new task and then click Add Item. This creates a document in Azure Cosmos DB with those properties.

  3. The page should update to display the newly created item in the ToDo list.

    Screenshot of the application with a new item in the ToDo list

  4. To complete a task, simply check the checkbox in the Complete column, and then click Update tasks. This updates the document you already created.

  5. To stop the application, press CTRL+C in the terminal window and then click Y to terminate the batch job.

Step 7: Deploy your application development project to Azure Websites

  1. If you haven't already, enable a git repository for your Azure Website. You can find instructions on how to do this in the Local Git Deployment to Azure App Service topic.
  2. Add your Azure Website as a git remote.

     git remote add azure https://username@your-azure-website.scm.azurewebsites.net:443/your-azure-website.git
    
  3. Deploy by pushing to the remote.

     git push azure master
    
  4. In a few seconds, git will finish publishing your web application and launch a browser where you can see your handiwork running in Azure!

    Congratulations! You have just built your first Node.js Express Web Application using Azure Cosmos DB and published it to Azure Websites.

    If you want to download or refer to the complete reference application for this tutorial, it can be downloaded from GitHub.

Next steps