Single Page Application Demo With Hapi.js, AngularJS And Azure Table

In my previous post Building an API in Node.js Using Hapi.js and Azure Table, we discussed about building an API in Node.js using Hapi.js as web development framework and Azure Table as the storage mechanism. This app has been updated with a Single Page Application (SPA) client, built with AngularJS. The updated app is available on github at https://github.com/shijuvar/HapiAzure.   

About the Demo App

I have developed this simple app for my personal use for just adding bookmarks with priority range from 1 to 5. I mainly created this for following github repositories with a priority so tat I can look on these bookmarks and evaluate it later based on the priority.

Here’s the technology stack used for the demo app:

  • REST API – Node.js and Hapi.js
  • Data Persistence – Microsoft Azure Table
  • Web App – AngularJS

The following Node.js modules are using for building the REST API.

  • hapi – HTTP Server framework, used for building the REST API app.
  • azure - Windows Azure Client Library for node, used for persisting data onto Azure Table storage.
  • joi - Object schema validation module for Hapi.js.
  • node-uuid – Generating unique id for Node.

Here’s the few screenshots of the app running as a Chrome app

image

image

API Endpoints

Currently, the API provides the following endpoints:

Path Method Functionality
/bookmarks Get Returns all bookmarks information from the Azure Table storage
/bookmarks/{rowkey} Get Get a single bookmark data for the given rowkey of Azure Table
/bookmarks Post Create new bookmark item onto Azure Table
/bookmarks/{rowkey} Put Update an existing bookmark item
/bookmarks/{rowkey} Delete Delete a bookmark item from the Azure Table

 

In the API project, the API endpoints are defined in the app.js file of routes folder

var Joi = require("joi");
var azure = require('azure');
var BookmarkController=require('./../controllers/bookmark');
var AzureTable=require('./../lib/azuretable');
var nconf = require('nconf');
nconf.env().file({ file: 'config.json'});
var tableName = nconf.get("TABLE_NAME");
var partitionKey = nconf.get("PARTITION_KEY");
var accountName = nconf.get("STORAGE_NAME");
var accountKey = nconf.get("STORAGE_KEY");
var tableService = azure.createTableService
                       (accountName, accountKey);
 
var azureTable = new AzureTable(azure.createTableService(
accountName, accountKey), tableName, partitionKey);
 
var bookmarkController = new BookmarkController(azureTable);
 
 
var routes =
[
{
method: 'GET',
path: '/bookmarks',
config: {
    handler: bookmarkController.getBookmarks
                             .bind(bookmarkController)
   }
},
{
method: 'GET',
path: '/bookmarks/{rowkey}',
config: {
    handler: bookmarkController.getBookmarkById
                             .bind(bookmarkController)
   }
},
{
method: 'POST',
path: '/bookmarks',
config: {
   handler: bookmarkController.insertBookmark
                              .bind(bookmarkController),
   validate: {
       payload: {
           title: Joi.string(),
           desc: Joi.string(),
           location: Joi.string(),
           priority: Joi.number().integer().min(1).max(5)
       } }
  }
},
{
method: 'PUT',
path: '/bookmarks/{rowkey}',
config: {
   handler: bookmarkController.updateBookmark
                       .bind(bookmarkController),
   validate: {
       payload: {
           title: Joi.string(),
           desc: Joi.string(),
           location: Joi.string(),
           priority: Joi.number().integer().min(1).max(5)
       } }
  }
},
{
method: 'DELETE',
path: '/bookmarks/{rowkey}',
config: {
   handler: bookmarkController.deleteBookmark
                                .bind(bookmarkController)
  }
}       
];
 
module.exports.routes = function (server) {
    server.route(routes);
};

REST API Mapping with AngularJS

The AngularJS service $resource is used for interacting with REST API services. The Angular service $resource is designed for working REST endpoints which lets the developers easily consuming RESTful server-side data sources.

bookmarkApp.factory("bookmarkService", ["$resource", 
function ($resource) {
    var baseUri='http://localhost:3000/bookmarks/';
 
 var Bookmarks = $resource(baseUri+':rowkey', 
{ rowkey: '@RowKey' }, { 'update': { method: 'PUT'} });
 
  var getAllBookmarks = function () {
       return Bookmarks.query();
   };
    var addNewBookmark = function (newBookmark) {
       return Bookmarks.save(newBookmark);
    };
   var updateBookmark = function (bookmark) {     
         return Bookmarks.update(bookmark);     
   };
   var getBookmarkByRowKey = function (rowKey) {
       return Bookmarks.get({rowkey:rowKey});
    };
   var deleteBookmark = function (rowKey) {
     return Bookmarks.delete({rowkey:rowKey});
 
   };
    return {
      getAll: getAllBookmarks,
       addNew: addNewBookmark,
       update: updateBookmark,
      getByKey: getBookmarkByRowKey,
       remove: deleteBookmark
  };
} ]);

Data Persistence with Microsoft Azure Table Storage

The Node.js module azure uses for working Azure Table storage. The code block below provides the implementations of CRUD operations against the Azure Table storage.

"use strict";
var azure = require('azure');
var uuid = require('node-uuid');
module.exports = AzureTable;
 
function AzureTable(storageClient, tableName, partitionKey) {
    this.storageClient = storageClient;
    this.tableName = tableName;
    this.partitionKey = partitionKey;
    this.storageClient.createTableIfNotExists(tableName,
   function tableCreated(error) {
        if(error) {
            throw error;
        }
    });
};
 
AzureTable.prototype = {
    find: function (query, callback) {
        var self = this;
        self.storageClient.queryEntities(query, 
       function entitiesQueried(error, entities) {
            if (error) {
                callback(error);
            } else {
                callback(null, entities);
            }
        });
    },
 
    addItem: function (item, callback) {
        var self = this;
        item.RowKey = uuid();
        item.PartitionKey = self.partitionKey;
        self.storageClient.insertEntity(self.tableName, item,
       function entityInserted(error) {
            if (error) {
                callback(error);
            }
            callback(null);
        });
    },
 
    updateItem: function (item, callback) {
        var self = this;
        self.storageClient.updateEntity(self.tableName, item, 
       function entityUpdated(error) {
            if (error) {
                callback(error);
            }
            callback(null);
        });
    },
 
    deleteItem: function (rowKey, callback) {
        var self = this;
        self.storageClient.deleteEntity(self.tableName
            , {
                PartitionKey : self.partitionKey
                , RowKey : rowKey
            }
            ,function entityDeleted(error) {
                if (error) {
                    callback(error);
                }
                callback(null);
            });
    }
}

Source Code

The demo app is available at https://github.com/shijuvar/HapiAzure

Creating Free Trial Account in Microsoft Azure

This demo app uses Microsoft Azure Storage which need a subscription in Azure. You will get free one month trial account in Azure which also provides free $200 credits to spending on all Azure services. You will get the free trial account from here.

You can follow me on Twitter @shijucv

No Comments