Accessing Azure Table Storage in Node.js Applications

Microsoft Azure Storage services provides features of storing NoSQL data in the Table Storage. The Azure Table Storage stores schema-less data using Key/Value pairs. Since the data store is schema-less, we can use Table Storage to store the data for those applications that are designed and targeted to flexible data capture. Although the data is stored as schema-less, it is easy to query the data using simple querying mechanism.

You can consider using Table storage where you need not to use complex joins, stored procedures for any data operations. 

Table Storage and Creating Resource Group and Storage Account


Following are some of the features of Azure Table Storage
  • Table is a collection of entities. These entities does not enforce any schema. This provides a flexibility to store data for different set of properties for entities.
  • Entity is a set of properties. Conceptually we can map an entity with a table row on relational database. The max size of entity in Table is 1 MB.
  • Property is a name/value pair. We can have max 252 user defined properties in entity and along with these there are 3 system properties present in entity. These properties are RowKey, PartitionKey and Timestamp.
  • PartitionKey, this is the key based on which the data in the Table Storage is stored in logical partition. This provides query optimization while retrieving data from table storage.
  • RowKey, is the unique identification of the entity in the table storage.        
To use Azure Storage, we need a Microsoft Azure Subscription. Please visit this link to create a free subscription. Make sure that you read all features and limits of using Azure Free Subscription. Once you have a subscription, login to the portal and you can start using its features.

To use Azure Storage, we need to create a Resource group. This is a logical group where all of our resources are stored.

Login on the portal and from the home page click on the Resource groups icon as shown in the image 1

Image 1: Creating Resource group

Once we click on the Resource groups, the Resource groups page will be displayed. On this page click on the +Add icon. Once we click on the +Add icon, the Create a resource group page will be displayed. On this page, enter information like Subscription, Resource group, and region as shown in the image 2  


Image 2: The resource group page 

On the Create resource group page, click on Review+create page, and a resource group will be created.

We need to create a Azure Storage account to access the Azure Storage services. From the home page click on the Create a resource icon as shown in the image 2


 Image 2: Create a resource 

Once the Create a resource icon is clicked, a new resource page will be displayed. On this page we have a search Textbox. In this Textbox, enter text as Storage Account - this will search for Storage account - blob, file, table, queue as shown in the image 3.


Image 3: Storage Account 

Once we select the Storage account option, the create Storage account page is displayed as shown in the image 4


Image 4: Create Storage Account 

On this page click on the Create button. This will navigate to the Create Storage Account page. On this page, we need to insert following information:

Subscription, this is the azure subscription which we are using to access Azure Services like Storage Account, SQL Azure, etc. 

Resource Group, this is the logical container under which our Storage account will be created. 

Location, this is the region where our storage account will be created and all storage services under it will be present.

Performance, is either Premium or Standard. The Premium is optimized for high transaction rates and having single-digit consistency in storage latency. Standard is optimized for high capacity and high throughput.  

Account Kind, this is General Purpose V1, V2 and BlobStorage.  The General purpose storage accounts provide unified storage for Blob, Table, Files and Queues. Whereas BlobStorage storage account provide storage only for Blobs.  

Replication, this is used for high availability and durability. 

Access Tier, this can be either cool or hot. The Cool access tier is best suitable for infrequently access data where as Hot access tier is recommended where the data is frequently accessed by the application. 

image 5 shows the parameters to set the while creating the Azure storage account


Image 5: The Azure storage account    

Once we click on Review+create button, the storage account will be created.  We can see all storage services offered by the storage account as shown in the image 6


Image 6: Storage services in the Storage Account

We have Containers for Blob storage, Table Storage, File Storage and Queue Storage. Now since we have a storage account, we can use it in our application. To connect to Azure Storage from the application, the application must be authenticated and authorized to access storage services, this can be possible using Access Keys.  We can copy these access keys from the Access Keys from the property blade of  the Storage account as shown in image 7.


Image 7: The Access Keys
   
Copy the Key1 and Connection String and paste it in the notepad so that we can use it in the application code. 

Creating the Node.js and Express.js application


In this article, we will use the Azure Table storage in the Node.js and Express.js application. 

Step 1: Create a new folder and name it as NodejsTableStorage. Open this folder in Visual Studio Code (VSCode).  Open Node.js command prompt and navigate to the NodejsTableStorage folder and run the command as shown in the listing 1 to create package.json

npm init -y

Listing 1: Creating package.json 

To use Azure Storage in Node.js application, we need to install azure-storage package. This is the Microsoft Azure Storage SDK and Node.js and JavaScript for browsers. This package provides methods to work with Azure Storage services. Install the azure-storage using the command shown in listing 2

npm install --save azure-storage

Listing 2: Installing azure-storage 

The azure-storage package contains following methods to work with the Table Storage:

createTableService(), the method returns Table Storage client object using Storage Account Name and the Access Key as input parameters to this method.

The TableStorageClient object provides following methods to perform Read/Write operations on the Table Storage. All these methods are asynchronous methods and accepts callback function as the last parameter to represents successful completion of execution or error occurred. 

  • createTableIfNotExist(), this method is used to create Azure Table if it is not already exists by accepting the table name as string. If it is already exists then it will be opened for performing Read/Write operations.
  • queryEntities(), this method accepts table name and an instance the TableQuery class as input parameters. The TableQuery class is used to define the query to be executed on the table storage so that data can be retrieved from the table based on the RowKey/Partition key, etc.
  • retrieveEntity(), this methods accepts, table name, partitionKey and RowKey to retrieve entity from the table.
  • insertEntity(), this method is used to insert new entity in the table. This method accepts table name and the entity as input parameters.
  • replaceEntity(), this method accepts the table name and entity input parameters. If the entity property values matched with the values those are present in the table then an existing entity will be replaced with the new entity value.
  • deleteEntity(), this method accepts table name and entity as input parameters. If the entity property values matched with the values those are already present in the table then an existing entity will be deleted.
  • insertOrReplaceEntity(),  this method accepts the table name and entity input parameters. If the entity property values matched with the values present in the table then an existing entity will be replaced with the new entity value. If the entity match is not found then the new entity will be inserted in the table.
  • mergeEntity(), this method accepts the table name and entity input parameters. This method will update the existing entity from the table by adding a new property in it.          

We will also install express, body-parser packages for creating REST APIs using Node.js and express. Run the command as shown in the listing 3 to install express and body-parser

npm install --save express body-parser

Listing 3: Installing express and body-parser

Step 2: In the project, add a new file and name it as keys.js. In this file, add a class. This class contains public properties for storing Storage Account Name and AccessKey. Add the code  as shown in listing 4 

class AccessKeys {
    constructor() {
        this.accountName = "[THE STORAGE ACCOUNT NAME]";
        this.accessKey = "[THE ACCESS KEY]";
    }
}

module.exports = AccessKeys;

Listing 4: The  AccessKeys class



The above values will be used for authenticating the Node.js application to access storage services.

Step 3:  Add a new file in the project and name this file as app.js. In this file, add code as shown in the listing 5

//1. use the required packages for the application 
const tableStore = require('azure-storage');
const express = require('express');
const bodyParser = require('body-parser');
//2. read access keys
const authStore = require('./keys');
const authObject = new authStore();
//3. create an instance of express
const instance = express();
// 4. add the middlewares
instance.use(bodyParser.json());
instance.use(bodyParser.urlencoded({ extended: false }));

Listing 5: The application code for loading required modules 

The code in the listing 5 loads required packages and also create an instance of the AccessKeys class and express class. We need these instances for authenticating the Node.js application against Table Storage and creating REST APIs respectively.

Modify the code in app.js by adding code in listing 6 to create a Azure Table it it is not already exist.

//5. the table name
const tableName = "Products";
//6. creating Table Client if not exist

const tableClient = tableStore.createTableService(authObject.accountName, authObject.accessKey);
tableClient.createTableIfNotExists(tableName, (error, result) => {
    if (error) {
        console.log(`Error Occured in table creation ${error.message}`);
    } else {
        console.log(`Result Table create success ${result.TableName} ${result.created}`);
    }
});

Listing 6: Create table if not exist

Modify the app.js by adding code to use get() method of express instance. This method will contains code for querying Azure Table to retrieve data based on the partition key. The code is shown in the listing 7

//7. retrive records from table based on PartitionKey
instance.get('/api/products/:categoryName', (request, response) => {
    var query = new tableStore.TableQuery()
        .where('PartitionKey eq ?', request.params.categoryName);
    //7a. execute the query based on the PartitionKey
    tableClient.queryEntities(tableName, query, null, (error, result, resp) => {
        if (error) {
            response.send(`Error Occured in table creation ${error.message}`);
        } else {
            response.json({ statusCode: 200, data: resp.body });
        }
    });
});

Listing 7: get() method to retrieve data from azure table

Modify the app.js by adding code in listing 8 to retrieve single entity from the azure table based on the partition key and row key. We are using get() method of express instance to contains logic to retrieve data

//8. retrive records from table based on RowKey and PartitionKey
instance.get('/api/products/:id/:categoryName', (request, response) => {
    let id = request.params.id;
    let partitionKey = request.params.categoryName;

    tableClient.retrieveEntity(tableName, partitionKey, id, (error, result, resp) => {
        if (error) {
            response.send(`Error Occured while retrieving data ${error.message}`);
        } else {
            response.json({ statusCode: 200, data: resp.body });
        }
    });
});

Listing 8: Retrieve single entity from azure table based on partition key and row key

Modify app.js for using post() method of express to accept entity data from the user and insert it in the azure table. The listing 9 shows logic of inserting entity in azure table

//9. post new entry in Table
instance.post('/api/products', (request, response) => {
    let prd = {
            PartitionKey: request.body.CategoryName,
            RowKey: request.body.ProductId,
            ProductName: request.body.ProductName,
            Manufacturer: request.body.Manufacturer,
            Price: request.body.Price
        }
    tableClient.insertEntity(tableName, prd, (error, result) => {
        if (error) {
            response.send(`Error Occured during entity insertion ${error.message}`);
        } else {
            response.json({ statusCode: 200, message: 'record added successfully', data: JSON.stringify(result) });
        }
    });
});

Listing 9: Inserting entity in azure table

Modify the app.js as shown in the code from listing 10. This code uses put() method of express and accepts rowkey and as id and partition key as categoryName in header. These values will be used to retrieve entity from the azure table. If the entity is present in the table, then it will be updated based on the entity data received from the put request.

// update entry from the Table based on RowKey and Partition Key
instance.put('/api/products/:id/:categoryName', (request, response) => {
    let id = request.params.id;
    let partitionKey = request.params.categoryName;
    // search the reocrd in the table based on the PartitionKey and RowKey
    tableClient.retrieveEntity(tableName, partitionKey, id, (error, result, resp) => {
        if (error) {
            response.send(`Error Occured while retrieving data record not found ${error.message}`);
        } else {
            // if the record is found then update
            let prd = {
                PartitionKey: request.body.CategoryName,
                RowKey: request.body.ProductId,
                ProductName: request.body.ProductName,
                Manufacturer: request.body.Manufacturer,
                Price: request.body.Price
            }

            tableClient.replaceEntity(tableName, prd, (error, result) => {
                if (error) {
                    response.send(`Error Occured during entity update ${error.message}`);
                } else {
                    response.json({ statusCode: 200, message: 'update successfull', data: result });
                }
            });

        }
    });
});

Listing 10: Update entity

Modify the app.js by adding code as shown in the listing 11. This code uses delete() method of express to delete entity. The row key as id and partition key as categoryName is accepted in header. The entity is searched in the azure table based on these values. If the entity is found, then it will be deleted from the table.

// delete record from the table based on RowKey and Partition Key
instance.delete('/api/products/:id/:categoryName', (request, response) => {
    let id = request.params.id;
    let partitionKey = request.params.categoryName;

    tableClient.retrieveEntity(tableName, partitionKey, id, (error, result, resp) => {
        if (error) {
            response.send(`Error Occured while retrieving data record not found ${error.message}`);
        } else {
            // if the record is found then update
            let prd = {
                    PartitionKey: partitionKey,
                    RowKey: id
                }
                // delete an entity 
            tableClient.deleteEntity(tableName, prd, (error, result) => {
                if (error) {
                    response.send(`Error Occured during entity delete ${error.message}`);
                } else {
                    response.json({ statusCode: 200, message: 'delete successfull', data: result });
                }
            });

        }
    });
});

Listing 11: Delete entity

Finally  add the code as  shown in listing 12 to listen in the port so that REST APIs will be available and client will be able to access it for performing Read/Write operations

instance.listen(8009, () => {
    console.log('Server Started on port 8009');
});

Listing 12: Listen requests for REST APIs

Step 4: From the command prompt run the following command to start the express server

node app.js

Open Postman / Advance REST client to perform operations. In the portal, open the Table listing by clicking on the Tables property in the property blade of the Storage account as shown in the image 8


Image 8: Tables in Storage account

Once you run the application using command explained in step 4, the following result will be displayed on the console as shown in the image 9. This shows that the table is created


Image 9: Table is created

Visit the portal and as shown in image 10, we can see the Products table created in Azure Table


Image 10: Table created 

Open Postman and make POST request using the following URL

http://localhost:8009/api/products

Set header value as 'Content-Type':'application/json'. The image 11 shows the POST request

Image 11: The POST request

Visit the portal and in the Storage Account properties blade locate Storage Explorer (preview) , it will show the storage explorer for Containers, Tables, Files, etc. Expand Tables, it will show the Products table created, click on click on the Products table, it will show the posted data as shown in the image 12


Image 12: The posted data in table storage 

Post some more records in the table as shown in the image 13


Image 13: The list of records added 

Lets make a HTTP get call using partition key as shown in the following URL
http://localhost:8009/api/products/Civil

It will show all entities from table for partition key as Civil as shown in image 14


Image 14: List of entities based on Civil as partition key

We can retrieve a single entity based on Row Key and Partition Key using following URL
http://localhost:8009/api/products/Prd001/Civil

The result will be displayed as shown in image 15


Image 15: The Single entity based on Row Key and Partition Key

Likewise update and delete functionality can be tested.

Conclusion: The azure-storage npm package provides a flexible and simple mechanism to access Azure Storage Services in Node.js applications and hence JavaScript full Stack application can easily make use of Azure Storage.





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: