Last week, I was conducting a session on MEAN Stack. In this training, I taught my students how to access MongoDB in an Express application.
One of the attendees asked me to create a demo on accessing CosmosDB using Node.js apps.
In this tutorial, I will show you the steps to do the same.
CosmosDB is a multi-model, globally distributed database service on Microsoft Azure. It provides NoSQL data storage using Table Storage, SQL API, MongoDB, Gremlin, etc. More information on this can be read here https://docs.microsoft.com/en-us/azure/cosmos-db/introduction. I have also written a post on using Azure CosmosDB with ASP.NET Core and Angular. You can read about it over here https://www.dotnetcurry.com/windows-azure/1395/cosmosdb-webapi-angular-client.
In modern web application development based on Isomorphic apps (pure JavaScript from Client-to-server e.g. MEAN/MERN Stack), the data storage is preferred as NoSQL data store. On top of that, if this data store is provided and managed by and on cloud, then such storage services are highly preferred.
CosmosDB is one such service provided on Microsoft Azure. In this tutorial, we will go through all the steps for accessing SQL API of CosmosDB using Node.js application. The application is implemented as shown in the following image
Image 1: Accessing CosmosDB using Node.js application.
We will implement a Node.js application using TypeScript. To implement the application, we need Microsoft Azure Cosmos JavaScript SDK. This is the library for SQL API of Azure CosmosDB Service. This can be installed using the following commands:
npm install --save @azure/cosmos
We need to create an Azure CosmosDB Account to create database and container. Please visit this link to read steps for creating Azure CosmosDB Account.
Once the Account is created, navigate to the newly created account and from the property pan, click on the Keys. This will show the URI and PRIMARY KEY. Copy this URI and PRIMARY KEY.
We need this to authenticate the Node.js application to access CosmosDB to perform operations like create database and container and perform CRUD operations. The following image shows URI and PRIMARY KEY.
Image 2: The URI and PRIMARY KEY for access the CosmosDB
To develop the application, we will Visual Studio Code (VSCode), a free IDE.
Step 1: Create a folder of the name nodecosmos on the drive and open this folder in VSCode. This folder will become workspace for the application.
Step 2: Open the command prompt and navigate to the nodecosmos folder. From the command prompt run the following command:
npm init -y
This will generate package.json file in the folder. In the package.json, in its dependencies section, mention the following packages. We will need these packages for writing code:
"@azure/cosmos": "^2.1.5",
"@types/express": "^4.17.0",
"@types/node": "^12.0.8",
"async": "^3.0.1",
"body-parser": "^1.19.0",
"cors": "^2.8.5",
"express": "^4.17.1",
"ts-node-dev": "^1.0.0-pre.40",
"typescript": "^3.5.1"
Step 3: Run the npm install command from the command prompt. This will install all the packages mentioned in dependencies.
The ts-node-dev package is a TypeScript execution package for Node.js. This will continuously execute the server side typescript code even during the code changes /updates. Modify the scripts section to set command to use ts-node-package for executing server-side as as shown in the following listing:
One of the attendees asked me to create a demo on accessing CosmosDB using Node.js apps.
In this tutorial, I will show you the steps to do the same.
CosmosDB is a multi-model, globally distributed database service on Microsoft Azure. It provides NoSQL data storage using Table Storage, SQL API, MongoDB, Gremlin, etc. More information on this can be read here https://docs.microsoft.com/en-us/azure/cosmos-db/introduction. I have also written a post on using Azure CosmosDB with ASP.NET Core and Angular. You can read about it over here https://www.dotnetcurry.com/windows-azure/1395/cosmosdb-webapi-angular-client.
In modern web application development based on Isomorphic apps (pure JavaScript from Client-to-server e.g. MEAN/MERN Stack), the data storage is preferred as NoSQL data store. On top of that, if this data store is provided and managed by and on cloud, then such storage services are highly preferred.
CosmosDB is one such service provided on Microsoft Azure. In this tutorial, we will go through all the steps for accessing SQL API of CosmosDB using Node.js application. The application is implemented as shown in the following image
Image 1: Accessing CosmosDB using Node.js application.
We will implement a Node.js application using TypeScript. To implement the application, we need Microsoft Azure Cosmos JavaScript SDK. This is the library for SQL API of Azure CosmosDB Service. This can be installed using the following commands:
npm install --save @azure/cosmos
We need to create an Azure CosmosDB Account to create database and container. Please visit this link to read steps for creating Azure CosmosDB Account.
Once the Account is created, navigate to the newly created account and from the property pan, click on the Keys. This will show the URI and PRIMARY KEY. Copy this URI and PRIMARY KEY.
We need this to authenticate the Node.js application to access CosmosDB to perform operations like create database and container and perform CRUD operations. The following image shows URI and PRIMARY KEY.
Image 2: The URI and PRIMARY KEY for access the CosmosDB
To develop the application, we will Visual Studio Code (VSCode), a free IDE.
Step 1: Create a folder of the name nodecosmos on the drive and open this folder in VSCode. This folder will become workspace for the application.
Step 2: Open the command prompt and navigate to the nodecosmos folder. From the command prompt run the following command:
npm init -y
This will generate package.json file in the folder. In the package.json, in its dependencies section, mention the following packages. We will need these packages for writing code:
"@azure/cosmos": "^2.1.5",
"@types/express": "^4.17.0",
"@types/node": "^12.0.8",
"async": "^3.0.1",
"body-parser": "^1.19.0",
"cors": "^2.8.5",
"express": "^4.17.1",
"ts-node-dev": "^1.0.0-pre.40",
"typescript": "^3.5.1"
Step 3: Run the npm install command from the command prompt. This will install all the packages mentioned in dependencies.
The ts-node-dev package is a TypeScript execution package for Node.js. This will continuously execute the server side typescript code even during the code changes /updates. Modify the scripts section to set command to use ts-node-package for executing server-side as as shown in the following listing:
"scripts": { "test": "echo \"Error: no test specified\" && exit 1", "dev": "ts-node-dev --respawn --transpileOnly ./server.ts" }
Listing 1: The scripts code to run the typescript file on server side.
Step 4: In the project add three folders and name them as appconfig, models and controllers. In the appconfig folder, add a new file and name it as config.ts. In this file, add the following code:
export const appConfig = { uri: '', authKey: ' ', databaseId: 'Seller', containerId:'ProductsCatelog' };
Listing 2: The Data Access configuration values copied from the Keys section of the CosmosDB property pane from Azure portal
The above object contains the URI and the PRIMARY Key for the CosmosDB Account copied from the Azure portal. This is needed to access CosmosDB service and performing CRUD operations. We are setting the databaseId name as Seller and containerId name as ProductsCatalog.
Step 4: In the models folder, add a new typescript file and name it as productdetails.ts. This file will contain ProductDetails class. This class will contain public members that will be used to create data (or records) in CosmosDB SQL API container. The following listing contains the class code:
export class ProductDetails { constructor( public ProductId: number, public ProductName: string, public CategoryName: string, public SubCategory: string, public Description: string, public Manufacturer: string, public Price: number ){} }
Listing 3: The ProductDetails class
// 1. importing the required APIs for performing operations with Cosmos import { CosmosClient, Database, Container } from '@azure/cosmos'; // 2. importing the application configuration constant for URL and Primary key import { appConfig } from "./../appconfig/config"; // 3. importing product details class import { ProductDetails } from './productsdetails'; // 4. The Data Access class to create various operations export class Dao { // 4.1. defining class level members private client: CosmosClient; private databaseId: string; private collectionId: string; private database: Database; private container: Container; // 4.2. constructor contains code to connect to CosmosDB Account constructor() { // 4.3. creating an instance of the CosmosClient and settings keys this.client = new CosmosClient({ endpoint: appConfig.uri, auth: { masterKey: appConfig.authKey } }); // ends here // 4.4. set the databaseId and containerId this.databaseId = appConfig.databaseId; this.collectionId = appConfig.containerId; //ends here } // 4.5. initialize the CosmosDB connections to create database and container async initDbAndContainer() { // create database if not exist const responseDb = await this.client.databases.createIfNotExists(
{id:this.databaseId}); this.database = responseDb.database; console.log(`Database Created ${this.database}`); // ends here // create a container if not exist const responseContainer = await this.database.containers.createIfNotExists({ id:this.collectionId }); this.container = responseContainer.container; console.log(`Container Created ${this.container}`); // ends here } // ends here // 4.6. add product async addProduct(product:ProductDetails) { const resp = await this.container.items.create(product); console.log(`In the addProduct ${JSON.stringify(resp.body)}`); return resp.body; } // ends here // 4.7. query to data async queryData() { const query = 'SELECT * FROM root'; if(!this.container){ throw new Error('The specified collection is not present'); } const result = await this.container.items.query(query).toArray(); return result.result; } // ends here // 4.8. code to update product async updateProduct(id:string,product:ProductDetails){ // get the document base on id parameter const record = await this.container.item(id).read(); console.log(`Record for update ${JSON.stringify(record.body)}`); // set the updated values record.body.ProductName = product.ProductName; record.body.CategoryName = product.CategoryName; record.body.SubCategory = product.SubCategory; record.body.Manufacturer = product.Manufacturer; record.body.Description = product.Description; record.body.Price = product.Price; console.log(`Record for update values ${JSON.stringify(record.body)}`); const updated = await this.container.item(id).replace(record.body); console.log(`Record after update ${JSON.stringify(updated.body)}`); return updated.body; } }
Listing 4: Code to perform operations like create database, container, items, etc.
- This imports the required APIs for performing operations with CosmosDB. The CosmosClient provides a client-side logical representation of the Azure CosmosDB database account. This is used to configure and execute requests in the Azure CosmosDB service. The Database object is used for reading and deleting an existing database. The Container object is used to perform operations like reading, updating, deleting container by id.
- This step imports the appconfig object. We have created this object in Step 4 for defining URI, Primary Key, Database and Container. We will be using this object in the current code for authenticating against the Azure CosmosDB account.
- This imports ProductDetails class
- The Dao class is used for performing operations as explained in following points
- This defines class level private members for CosmosClient, DatabaseId, CollectonId, database and container name.
- The constructor is defined to initialize the environment for CosmosDB service.
- This step is used to create an instance of CosmosDB client using URI and PRIMARY key.
- Here the default names for Database and Container is set. We are using database name as Seller and Container name as ProductCatelog
- The initDbAnContainer() method will contain code for creating Database and Container if they are not already exists. This is an asynchronous method.
- The addProduct() is an asynchronous method that accepts the ProductDetails object and create a new item into the container.
- The queryData() is an asynchronous method. This method is used to query to the container to read items from it.
- The updateProduct() is an asynchronous method that is used to update the product based on the search criteria as id. This method also accepts ProductDetails object to update the searched product based on id.
Step 6: In the controllers folder, add a new file and name it as appcontroller.ts. Add the following code in it:
// 1. import Request and Response objects from express import { Request, Response } from "express"; // 2. import the ProductDetails class import { ProductDetails } from './../models/productsdetails'; // 3. import the Dao class import { Dao } from "./../models/dao"; // 4. The AppController class export class AppController { // 4.1 Define an instance of the Dao class private dao: Dao; constructor() { this.dao = new Dao(); } // 4.2 the init method accepts initDbAndContainer()
method to create database and container async init() { await this.dao.initDbAndContainer(); } // 4.3 this method accepts queryData() method to return query result async getData(request: Request, response: Response) { const products = await this.dao.queryData(); response.send({ data: products }); } // 4.4 this method accepts the ProductDetails in the request body and
create product by accepting addProduct() method async addProduct(request: Request, response: Response) { const body = request.body; console.log(`Received Body ${JSON.stringify(body)}`); const product = new ProductDetails( body.ProductId, body.ProductName, body.CategoryName, body.SubCategory, body.Description, body.Manyfacturer, body.Price ); console.log(`Product ${JSON.stringify(product)}`); let data = await this.dao.addProduct(product); response.send({ data: data }); } // 4.5 this method is used to update the product async updateProduct(request: Request, response: Response) { const body = request.body; const id = request.params.id; console.log(`Received Body ${JSON.stringify(body)}`); const product = new ProductDetails( body.ProductId, body.ProductName, body.CategoryName, body.SubCategory, body.Description, body.Manyfacturer, body.Price ); console.log(`Product ${JSON.stringify(product)}`); let data = await this.dao.updateProduct(id,product); response.send({ data: data }); } }
Listings 5: The AppController class that accepts Dao class
- imports the Request and Response objects from express package. These objects represents express REST API requests and responses.
- imports ProductDetails class
- The Dao class is imported so that its methods can be accessed.
- The AppController class. This class contains all asynchronous methods and does the following:
- The constructor creates an instance of the Dao class
- init() is an asynchronous method. This method access initDbAndContainer() method of the Dao class to create database and container.
- getData() is an asynchronous method. This method accepts Request and Response objects. This method accesses queryData() method from the Dao class and responds with the resultant products.
- addProduct() is an asynchronous method. This method accepts the ProductDetails in request body. The data from request body is read and stored in the local ProductDetails object. This object is then passed to the addProduct() method of the Dao class to create new product in the container. This method responds the created product in the container.
- updateProduct() is an asynchronous method. This method accepts the ProductDetails in request body. The data from request body is read and stored in the local ProductDetails object. The request contains the id parameter. This parameter is used to search the product to be updated. This method responds the updated product in the container.
Step 7: In the root of the project, add a new file and name it as server.ts. In this file, add the following code:
// 1. imports express, bodyparser and cors packages import * as express from 'express'; import * as bodyParser from 'body-parser'; import * as cors from 'cors'; // 2 imports the AppController import { AppController } from "./controllers/appcontroller"; // 3. an instance of the express and adding bodyParser and cors middlewares const instance = express(); instance.use(bodyParser.json()); instance.use(bodyParser.urlencoded({ extended: false })); instance.use(cors()); // 4. instance of the AppController const appController = new AppController(); // 5. accessing the init() method appController.init().then(() => {
console.log('database and container created Successfully'); }).catch(() => { console.log('database and container creation failed'); process.exit(1); }); // 6. API methods for get, post and put instance.get('/api/products',
(req,res,next)=>appController.getData(req,res).catch(next)); instance.post('/api/products',
(req,res,next)=>appController.addProduct(req,res).catch(next)); instance.put('/api/products/:id',
(req,res,next)=>appController.updateProduct(req,res).catch(next)); // 7. listen on the port instance.listen(9078, () => { console.log('started on port 9078'); });Listing 6: The server code with REST API
The above code have following specifications (Note that the following line numbers matched with comments applied on code):
- Imports required packages like express, body-parser and cors.
- Imports AppController class
- Defines instance of express and set required middlewares.
- Instance of the AppController class
- init() method of the AppController class is accessed. This creates database and the container.
- REST API methods are used to access various methods from the AppController class.
- Starts listening on the port
Step 8: Run the application by running the following command on the command prompt:
npm run dev
This will show the following result:
Image 3: The result showing database and container created
The database and the container is created. Visit back to the portal, it will show the Database and container created as shown in the following image
Image 4: The portal shows the Database and the container
Step 8: Open Postman or Fiddler client (or any other REST client) to test the REST API. I am using Postman. Open Postman and enter the following URL in the address bar
http://localhost:9078/api/products
and make a Get request, it will show the result as empty result. Now make a post request with data as shown in the following image:
and make a Get request, it will show the result as empty result. Now make a post request with data as shown in the following image:
Image 9: The Post request
The following image will show the record added in CosmosDB :
Image 10: The data in the CosmosDB
Conclusion:
In this tutorial, we saw the steps for accessing SQL API of CosmosDB using Node.js
Tweet
No comments:
Post a Comment