Windows Azure and Node.js

As Rob Blackwell pointed outNode.js has a clear part to play in cloud computing. But what does an application powered by Node look like? Can we build something entirely with javascript and run it on Azure now? I decided to explore this by building a simple todo application.

The application lists the items in your todo list, new items are added by typing in the input box, and pressing ‘Add':


Clicking on an item shows a different page, and asks you if you want to delete it.


Very simple. There is no security, no error trapping, and it looks awful in Internet Explorer – but it proves a concept. Let’s dig under the covers.

Design Tenets

  1. The application running in the browser should be a static HTML file. This allows us to consider offline use with HTML5 storage if available.
  2. When the application is running, only data (JSON) should be exchanged with the server. We should not be rendering markup on the server. This keeps our bandwidth requirements to a minimum.
  3. The only code I am going to write is javascript.

The User Interface

The application is a single static HTML file, and uses a browser-based MVC architecture. The application uses a number of standard javascript libraries. In particular, these libraries are used:

Backbone.js

Backbone provides the client-side routing in the application. All links in the application link to anchors (i.e. they only modify parts of the URL after the  ‘#’ ). These links do not cause a round trip back to the server, instead, backbone picks them up and routes you to a controller. So in this application I have these two routes set up:

var AppRouter = Backbone.Router.extend({
  routes: {
    "/view/:id": "viewItem",
    "*actions": "defaultRoute"
  },
  defaultRoute: function(actions){
    two10.actions.defaultAction();
  },
  viewItem: function(id){
    two10.actions.view(id);
  }
});
var router = new AppRouter;

So when the URL matches the first route (i.e.  ‘…/#/view/1234′) the ‘two10.actions.view’ function is called, passing in ‘1234’. Otherwise the second route is matched, and ‘two10.actions.defaultAction’ is called.

This simple piece of functionality is very powerful, as it means my application can be bookmarked. It also means I don’t have a complicated web of javascript functions dotted around my application, I mostly just use links like this

  <a href="#/view/1234">View</a>

JQuery Templates

My controllers need to write html out for the user to see. To do this I use JQuery Templates. There are dozens of options for templating in javascript, I’m just sticking with what I know. The template to display the todo item, with a delete button looks like this (I have simplified the markup):

<script id="view" type="text/x-jquery-tmpl">
  <div>
    <div>
      <h1>${item.get("name")}</h1>
      <a href="#">Back</a>
    </div>
    <div>
      <p>${item.get("name")}</p>
      <p><a href="javascript:two10.actions._delete('${item.cid}');">Delete</a></p>
    </div>
  </div>
</script>

Note how in the link for the delete button bypasses the routing, and invokes the controller directly. This is because I want to emulate a post, and I don’t want the URL for deleting the item to appear in the browser or the routing table.

To actually render the view, my code needs to pass in the model (i.e. the object which holds the todo item):

var item = two10.data.byId(id);        // retrieve the item out of the in-memory collection
$("#body").html($("view").tmpl(item)); // render the template, passing in the item

JQuery Mobile

I attempted at first to use JQuery Mobile to enhance my markup (and make my application look pretty). However there is also a routing capability with JQuery Mobile, which conflicts with Backbone. Despite efforts to disable it, I decided to just use the CSS from JQuery Mobile, and put the enhanced markup in my templates. Whilst this requires more effort when writing the templates, it probably improves runtime performance by reducing the amount of DOM manipulation required in the browser.

Server Side

Serving the page

The server side code is a node.js script. The first thing it’s required to do is to serve up the HTML page to the browser.

var http = require('http');
var fs = require('fs');
http.createServer(function (request, response) {
  switch (request.url) {
    case '/':
      fs.readFile('./default.htm', function(error, content) {
        if (error) {
          response.writeHead(500);
          response.end();
        }
        else {
          response.writeHead(200, { 'Content-Type': 'text/html' });
          response.end(content, 'utf-8');
        }
      });
      break;
 ...

Connecting to Azure Storage

The todo items should be persisted in Azure Table Storage. To do this, I forked a prototype version of the node-azure library which makes it straight forward to save/retrieve records in table storage using these methods:

function insert_entity(account, tablename, data, callback)
function get_entity(account, tablename, partitionKey, rowKey, callback)

The code running on the browser does a GET as soon as the application starts, node retrieves the data from table storage, and just returns it back to the browser. Every time the user changes the items in the todo list, the application does a POST to node, which just saves the data in table storage.

Deploying to Azure

I tried to use the iisnode technique to get node running on a web role, and failed. Instead, I added node as a background startup task to a worker role. These are the steps to set it up:

1. Create a Worker Role, open up port 80, and register a startup command. Your Service Definition file should look like this:

<?xml version="1.0" encoding="utf-8"?>
<ServiceDefinition name="WindowsAzureProject2" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition">
  <WorkerRole name="WorkerRole1" vmsize="ExtraSmall">
    <Startup>
      <Task commandLine="Startup.cmd" executionContext="elevated" taskType="background"/>
    </Startup>
    <Endpoints>
      <InputEndpoint name="Endpoint1" protocol="tcp" port="80" />
    </Endpoints>
  </WorkerRole>
</ServiceDefinition>

2. Create a startup.cmd file, and add it to your Worker Role. The command should contain the instruction to start node, passing in your script:

node server.js

3. Add node.exe, and all of your javascript/html files to your Worker Role project, ensure all these files are set to copy local (node dependencies can be added to a ‘node_modules’ sub-folder).

4. Deploy, and stand back.

Conclusion

Developing in javascript is fun, and more productive than I had thought.

I would like to see more investment in node on the windows platform, currently the Node Package Manager (NPM) hasn’t been ported, (read how to do this manually) and it would be better to have node running under IIS than a background task in a Worker Role.

The power of  routing and templating in the browser shouldn’t be underestimated. With the addition of some HTML5 features, this technique could seriously compete with Silverlight.

I don’t think this stack is the solution for everything, but if you want multi-device support, good scalability and low bandwidth requirements, you should certainly consider it. Bear in mind though, node is in it’s infancy, this is the bleeding edge.

Source

The entire source can be downloaded here.

About these ads