Using Mongoose Framework to Access Azure Cosmos DB MongoDB API

Cosmos DB is a globally-distributed, multi-model database service on Microsoft Azure. Cosmos DB provides database services like SQL API, MongoDB API, Cassandra, Gremlin for storing NoSQL data.

We can create a Cosmos DB account to create a database and perform Read/Write operations on these databases using various SDKs like .NET, .NET Core, Node.js, JAVA etc. If you are working on  theJavaScript stack for building isomorphic apps using Node.js and other JavaScript frameworks like Angular, Vue.js or libraries like React.js, then you will mostly prefer to use MongoDB database for storing data. 

Most of the isomorphic apps those which are using MongoDB database, uses Mongoose framework to perform CRUD operations on the MongoDB Database. On Microsoft Azure, if you are using MongoDB API database service of Cosmos DB to store data,   there is a support available for Mongoose framework. This provides a similar coding experience of using MongoDB in on-premise applications. 

In this article, we will see how to access Azure Cosmos DB for MongoDB API using Node.js, Express application. Figure 1 explains the application structure


Image 1: The Node.js + Express application accessing Azure Cosmos DB for MongoDB API 

We will implement the application using Microsoft Visual Studio code (VSCode). This is a free IDE by Microsoft. You can download this IDE from this link. We need Node.js to run the application. The Node.js can be downloaded from this link.  

To implement this application, we need to create Cosmos DB Account. 

Step 1: Open the Azure Portal and login with your credentials. If you do not have Azure subscription then please visit this link to create a free account. Once you login on the portal, you can create a new resource group by clicking on Resource group icon as shown in image 2



Image 2: The Home page Resource group icon

Once the icon is clicked, it will show a list of resource groups already available under your subscription. On this page click on Add button as shown in image 3, to load the resource group create page


Image 3: List of resource groups

Once the Add button is clicked, the Create a resource group page will be displayed. You can create resource group by entering information for subscription, resource group and Region as shown in image 4.


Image 4: Creating resource group 

Name the resource group as resgp. Click on Review+create button to create a resource group.

Once the resource group is created, we can now create a Cosmos DB database account. Visit  the home page and click on Create a resource icon as shown in image 5

Image 5: Create a resource  

Once a Create a resource icon is clicked, the New resources listing will be displayed as shown in image 6. Click on the Azure Cosmos DB icon.

Image 6: Creating Cosmos DB 

On the Create Azure Cosmos DB Account page, enter information as shown in image 7



Image 7: The Azure Cosmos DB Account 

As per the image 7, enter the resource group name as resgp,  set the Cosmos DB Account name as testdatadb and sect the API as Azure Cosmos DB for MongoDB API. The version selected will be 3.6 and the location is West US. 

Click on Review+create button to create a Cosmos DB Account.  Once the account is created, we need to use the Connection string, user name and password of the Cosmos DB Account to authenticate the client application to connect with it. The property blade of the testdatadb Cosmos DB account shows the connection string and other information for authentication. This can be seen as shown in image 8.



Image 8: The connection string and other authentication information

Click on the Data Explorer of the property blade of the testdatadb. This shows all databases and collections in the MongoDB. To create a new database click on the New Collection drop down in the data explorer as shown in the image 9


Image 9: Create new database    

Create a new database of name eShop as shown in image 10

Image 10: The eShop database

So we have now created database! Now its time to create collection and perform CRUD operations with it. We will do this using Node.js + Express.js + Mongoose.

Step 2: Create a folder on the local drive of name cosmosmongoose. Open this folder in VSCode. Open Node.js command prompt and navigate to the cosmosmongoose folder. From the command prompt run commands as shown in the listing 1. These commands are used to create package.js file and installing Expresss.jsm Mongoose, etc packages respectively.

npm init -y 

npm install --save express mongoose body-parser

Listing 1: Commands to install Express.js and Mongoose, etc         

Step 3: In the project add a new file and name it as config.js. In this file add a ES 6 class to initialize properties for Cosmos DB User, Password, Database name, etc.  add the code in this file as shown in the listing listing 2.


class DbConfigurations {
    constructor() {
        this.COSMOS_USER = "testdatadb";
        this.COSMOS_PASSWORD = "";
        this.COSMOS_DBNAME = "eShop";
        this.COSMOS_HOST = "testdatadb.mongo.cosmos.azure.com";
        this.COSMOS_PORT = "10255"
    }
}
module.exports = DbConfigurations;

Listing 2: The config class  

The DbConfigurations class is exported as custom Node.js module.

Step 4: In the project add a new file and name it as server.js. Add the code for loading modules in the Node.js application e.g. express, body-parser, etc and also DbConfigurations class which is exported as custom Node.js module as shown in listing 3

const express = require('express');
const bodyParser = require('body-parser');
const mongoose = require('mongoose');
const config = require('./config');

Listing 3: Loading packages 

Let's define an instances of express object and DbConfigurations class. We need to configure bodyParser middleware that will be used to parse the HTTP POST request body  to the express REST API. The most important part of the code here is the connection string to the Cosmos DB MongoDB API. The format of the connection string is as follows

"mongodb://<cosmoshost>:<port>/<database-name>?ssl=true&replicaSet=globaldb&retrywrites=false"

It is very important to set retrywrites=false to make sure that Write operations are successfully takes place on the collection. Add the code as shown on the listing 4

const instance = express();

instance.use(bodyParser.json());
instance.use(bodyParser.urlencoded({ extended: false }));
const dbConfig = new config();
const connString = `mongodb://${dbConfig.COSMOS_HOST}:${dbConfig.COSMOS_PORT}/
${dbConfig.COSMOS_DBNAME}?ssl=true&replicaSet=globaldb&
retrywrites=false`

Listing 4: The express instance and connection string

Lets define Mongoose Schema and collection that map with the schema. This collection will be generated when we insert new document. Add the code as shown in the listing 5

const ProductsSchema = new mongoose.Schema({
    ProductRowId: Number,
    ProductId: String,
    ProductName: String,
    Categoryame: String,
    Description: String,
    Price: Number
});


const model = mongoose.model("Products", ProductsSchema, "Products");

Listing 5: The Mongoose schema and collection

Add the code as shown in the listing 6. Here we are connecting to the Cosmos DB account and the MongoDB API in it. To connect to the MongoDB API we need to pass Username and password

mongoose.connect(connString, {
        useUnifiedTopology: true,
        useNewUrlParser: true,
        auth: {
            user: dbConfig.COSMOS_USER,
            password: dbConfig.COSMOS_PASSWORD
        }
    })
    .then(() => console.log('Connection to CosmosDB successful'))
    .catch((err) => console.error(err));

Listing 6: Connect to the MongoDB

Once the connection is established with MongoDB API in Cosmos DB with authentication, we can perform CRUD operations. We will be implementing it using Express REST APIs as shown in the code in listing 7

instance.get('/api/products', (req, resp) => {
    model.find((error, documents) => {
        if (error) {
            resp.send({ statusCode: 500, message: error });
        }
        resp.json({ status: 200, data: documents });
    });
});

instance.get('/api/products/:id', (req, resp) => {
    let id = req.params.id;
    model.findOne({ _id: id }, (error, documents) => {
        if (error) {
            resp.send({ statusCode: 500, message: error });
        } else {
            resp.send({ status: 200, data: documents });
        }
    });
});

instance.post('/api/products', (req, resp) => {
    let prd = {
        ProductRowId: req.body.ProductRowId,
        ProductId: req.body.ProductId,
        ProductName: req.body.ProductName,
        CategoryName: req.body.CategoryName,
        Manufacturer: req.body.Manufacturer,
        Description: req.body.Description,
        Price: req.body.Price
    };
    model.create(prd, (error, doc) => {
        if (error) {
            resp.send({ statusCode: 500, message: error });
        } else {
            resp.send({ status: 200, data: doc });
        }
    });
});

instance.put('/api/products/:id', (req, resp) => {
    let id = req.params.id;
    let prd = {
        ProductRowId: req.body.ProductRowId,
        ProductId: req.body.ProductId,
        ProductName: req.body.ProductName,
        CategoryName: req.body.CategoryName,
        Manufacturer: req.body.Manufacturer,
        Description: req.body.Description,
        Price: req.body.Price
    };
    model.findOneAndUpdate({ _id: id }, prd, (error, doc) => {
        if (error) {
            resp.send({ statusCode: 500, message: error });
        } else {
            resp.send({ status: 200, data: doc });
        }
    });
});


instance.delete('/api/products/:id', (req, resp) => {
    let id = req.params.id;

    model.findOneAndDelete({ _id: id }, (error, doc) => {
        if (error) {
            resp.send({ statusCode: 500, message: error });
        } else {
            resp.send({ status: 200, data: doc });
        }
    });
});

instance.listen(8908, () => {
    console.log('Started listening on Port 8908');
});

console.log('done');

Listing 7: The REST API code for CRUD operations

We are implementing HTTP methods for REST APIs. We are exposing these APIs on port 8908.
From the Node.js command prompt that we opened in Step 2, run the following command to run the server

node server.js

This will start listening on port 8908. To test these API, use Postman or Advanced REST Client (or any other tool). In this article we will be using Postmon.

Create a post request as shown in Image 11.


Image 11: The Post request

Click on the Send button of the Postman. The data will be posted to the REST API. The Node.js application will use Mongoose Framework to connect to MongoDB API on Cosmos DB and create a collection of name Products. The new document will added in the collection as shown in the image 12

Image 12: The collection created in MongoDB API in Cosmos DB 

Likewise you can test GET/PUT/DELETE method

Conclusion: We saw how the Mongoose Framework support with MongoDB API in Azure Cosmos DB provides a simple mechanism to build the JavaScript Stack Isomorphic apps on Azure without much code changes in our existing applications.






About The Author

Mahesh Sabnis is a Microsoft MVP having over 18 years of experience in IT education and development. He is a Microsoft Certified Trainer (MCT) since 2005 and has conducted various Corporate Training programs for .NET Technologies (all versions). He also blogs regularly at DotNetCurry.com. Follow him on twitter @maheshdotnet

No comments: