December 2015

Volume 30 Number 13

Data Points - Aurelia Meets DocumentDB: A Matchmaker’s Journey, Part 2

By Julie Lerman

Julie LermanThe new JavaScript framework, Aurelia, and the NoSQL document database service, Azure DocumentDB, are two different technologies that have captured my interest lately. In June, I explored DocumentDB at a high level (msdn.com/magazine/mt147238). Then, in September, I played with data binding in Aurelia (msdn.com/magazine/mt422580). Still intrigued by both technologies, I thought I’d like to put the two together, using Aurelia as the front end of an application and DocumentDB as the data store. While my curiosity gave me some advantage (persistence), my limited experience with these two new technologies, and with JavaScript frameworks in general, set me along a number of dead-end attempts to combine them. This was exacerbated by the fact that nobody had done this combo before—at least not publicly. These failed attempts were documented in my November column (msdn.com/magazine/mt620011), the first of this two-part series. One obvious and easy path I chose to skip was to use my existing Web API that wrapped interaction with DocumentDB in place of the Web API I worked with in my first Aurelia article. I made this choice because there was no challenge in it—and therefore not much fun.

I finally came to the decision to use a different server-side solution: Node.js. There’s an existing example that uses the DocumentDB Node.js SDK and a different front end. I inspected it carefully and shared what I learned with you in the second half of my November column. Then I set about recreating my Ninja example from the September column, this time using Node.js as my go-between from the Aurelia front end to DocumentDB. The stumbling blocks were many, and they went far beyond data-related issues. I had loads of support from members of the Aurelia core team and especially from Patrick Walters (github.com/pwkad), who not only helped ensure I was taking advantage of Aurelia features, but also indirectly forced me to further hone my Git skills. What I’ll focus on here are the pieces of the solution that involve accessing, updating and binding the data. The ReadMe file that comes with the accompanying downloadable example explains how you can set up your own DocumentDB and import the data for this solution. It also explains how to set up the requirements to run the sample.

Solution Architecture

First, let’s take a look at the overall architecture of this solution, as shown in Figure 1.

The Overall Structure of This Solution
Figure 1 The Overall Structure of This Solution

Aurelia is a client-side framework so it’s here only to handle client-side requirements, such as rendering HTML based on the view and view-model pairings, as well as routing, data binding and other relevant tasks. (Note that all of the client-side code can be debugged in developer tools available in browsers such as Internet Explorer and Chrome.) In the solution’s path, all of the client-side code is in the public/app/src folder, while the server-side code is in the root folder. When the Web site is running, the client-side files are compiled by Aurelia to ensure browser-compatibility and distributed into a folder called “dist.” These files are what get sent to the client.

The server-side code should be familiar if you’ve done Web development in the Microsoft .NET Framework (closer to my own background). In the .NET environment, your codebehind from WebForms, controllers and other logic is traditionally compiled into DLLs that live on the server. Of course, much of this changes with ASP.NET 5 (which I now feel more prepared for, thanks to working on this particular project). My Aurelia project also contains server-side code, but even that is JavaScript code by way of Node.js. This code isn’t compiled into a DLL but remains in its own individual files. More important, it doesn’t get pushed down to the client and is therefore not readily exposed (this is, of course, always quite dependent on security measures). As I described last time, the driving reason for wanting code that stays on the server is that I needed a way to store my credentials for interacting with DocumentDB. Because I wanted to try out the Node.js path, the aforementioned SDK made conversing with DocumentDB a lot easier. That I had the original DocumentDB Node example to lean on for some basics on how to use the SDK was a huge help.

My server-side code (which I’ll refer to as my API) consists of four key ingredients:

  • An api.js file that acts as a router to ensure my API functions can be easily found.
  • My core module, ninjas.js, containing the API functions: getNinjas, getNinja and update­Details.
  • A controller class, DocDbDao (called by those API functions) that performs the interaction with DocumentDb.
  • A DocumentDb utility file that knows how to ensure the relevant database and collection exist.

Wiring up the Client, Server and Cloud

In my earlier Aurelia sample, the method to get all ninjas made a direct HTTP call to an ASP.NET Web API that used .NET and Entity Framework to interact with a SQL Server database:

retrieveNinjas() {
  return this.http.createRequest
    ("/ninjas/?page=" + this.currentPage + "&pageSize=100&query=" +
      this.searchEntry)
      .asGet().send().then(response => {
        this.ninjas = response.content;
    });
  }

The results were passed to a pairing of the client side view and view-model (ninjaList.js and ninjaList.html) that outputs the page shown in Figure 2.

The Ninja Listing in My Aurelia Web Site
Figure 2 The Ninja Listing in My Aurelia Web Site

In the new solution, this method, renamed to getNinjas, now calls into my server-side Node.js API. I’m using the more advanced httpClient.fetch (bit.ly/1M8EmnY) instead of httpClient this time to make the call:

getNinjas(params){
  return this.httpClient.fetch(`/ninjas?q=${params}`)
    .then(response => response.json())
    .then(ninjas => this.ninjas = ninjas);
}

I’ve configured httpClient elsewhere to know the base URL.

Notice that my fetch method is calling a URI that includes the term ninjas. But this is not referring to my ninjas.js in the server-side API. It could be /foo—just a random reference that will be resolved by my server-side router. My server-side router (which uses Express, not Aurelia, because Aurelia only handles the client side) specifies that calls to api/ninjas be routed to the getNinjas function of the ninjas module. Here’s the code from api.js where this is defined:

router.get('/api/ninjas', function (request, response) {
  ninjas.getNinjas(req, res);
});

Now my getNinjas function (Figure 3) uses string interpolation (mzl.la/1fhuSIg) to build up a string to represent the SQL for querying DocumentDB, then asks the controller to execute the query and return the results. If a filter on the name property has been requested in the UI, I will append a WHERE clause to the query. The main query projects only the relevant properties I’ll need on the page, including the ID. (See more about DocumentDB projection queries at documentdb.com/sql/demo.) The query is passed into the find method of the docDbDao controller. The find method, which is unchanged from the origi­nal I described in the first part of this series, uses the Node.js SDK along with the credentials stored in config.js to query the database for the ninjas. getNinjas then takes those results and returns them to the client that requested them. Note that although the call to DocumentDB does return results as JSON, I still need to explicitly use the response.json function to pass the results back. This alerts the caller that the results are JSON-formatted.

Figure 3 The getNinjas Function in the ninjas.js Server-Side Module

getNinjas: function (request, response) {
  var self = this;
  var q = '';
  if (request.query.q != "undefined" && req.query.q > "") {
    q= `WHERE CONTAINS(ninja.Name,'${req.query.q}')`;
  }
  var querySpec = {
    query:
   `SELECT ninja.id, ninja.Name,ninja.ServedInOniwaban,
     ninja.DateOfBirth FROM ninja ${q}`
  };
  self.docDbDao.find(querySpec, function (err, items) {
    if (err) {
      // TODO: err handling
    } else {
      response.json(items);
    }
  })
},

Client-Side Response to an Edit Request

As you can see in Figure 2, it’s then possible to edit a ninja by clicking on the pencil icon. Here’s a link I built up in the page markup:

<a href="#/ninjas/${ninja.id}" class=
  "btn btn-default btn-sm">
  <span class="glyphicon glyphicon-pencil" />
</a>

By clicking the edit icon for my first row, whose ID happens to be “1,” I get this URL:

http://localhost:9000/app/#/ninjas/1

Using the Aurelia routing feature on the client side in app.js, I’ve specified that when this URL pattern is requested, it should then call the edit module and pass in the id to the activate method of the edit view-model as a parameter, indicated by the wildcard (*Id):

{ route: 'ninjas/*Id', moduleId: 'edit', title:'Edit Ninja' },

Edit refers to the client-side edit.js module that’s paired with the edit.html view. The activate function of my edit module then calls another function in this module named retrieveNinja, passing in the requested Id:

retrieveNinja(id) {
  return this.httpClient.fetch(`/ninjas/${id}`)
    .then(response => response.json())
    .then(ninja => this.ninja = ninja);
}

Passing the Edit Request to the Server-Side API

Again, I’m using httpClient.fetch to request api/ninjas/[id] (in my case api/ninjas/1) from the API. The server-side router says that when a request with this pattern comes in, it should route to the getNinja function of the ninjas module. Here’s what that routing looks like:

router.get('/api/ninjas/:id', function(request, response) {
  ninjas.getNinja(request, response);
});

The getNinja method then makes another request to the docDbDao controller, this time to the getItem function, to get the ninja data from DocumentDb. The results are JSON documents that are stored in my DocumentDB database, as shown in Figure 4:

Figure 4 The Document I Requested, Which Is Stored in my DocumentDb Ninjas Collection

{
  "id": "1",
  "Name": "Kacy Catanzaro",
  "ServedInOniwaban": false,
  "Clan": "American Ninja Warriors",
  "Equipment": [
    {
      "EquipmentName": "Muscles",
      "EquipmentType": "Tool"
    },
    {
      "EquipmentName": "Spunk",
      "EquipmentType": "Tool"
    }
  ],
  "DateOfBirth": "1/14/1990",
  "DateCreated": "2015-08-10T20:35:09.7600000",
  "DateModified": 1444152912328
}

Passing the Results Back to the Client

This JSON object is returned to the ninjas.getNinja function, which then returns it to the caller, in this case the edit.js module on the client. Aurelia then binds edit.js to the edit.html template and outputs a page, which is designed to display this graph.

The edit view allows users to modify four pieces of data: the ninja’s name and birthdate (both strings), the clan (a dropdown) and whether the ninja served in Oniwaban. The Clan dropdown uses a special type of Web component called a custom element. The Aurelia implementation of the custom element spec is unique. Because I’m using that feature to bind data, and this is a data column, let me show you how that’s done.

Data Bind with an Aurelia Custom Element

Like other view and view-model pairs in Aurelia, a custom element is composed of a view for the markup and a view-model for the logic. Figure 5 shows the third file that’s involved, clans.js, which provides a list of clans.

Figure 5 Clans.js

export function getClans(){
  var clans = [], propertyName;
  for(propertyName in clansObject) {
    if (clansObject.hasOwnProperty(propertyName)) {
      clans.push({ code: clansObject[propertyName], name: propertyName });
    }
  }
  return clans;
}
var clansObject = {
  "American Ninja Warriors": "anj",
  "Vermont Clan": "vc",
  "Turtles": "t"
};

The view for this element (dropdown.html) uses a Bootstrap “select” element, which I still think of as a dropdown:

<template>
  <select value.bind="selectedClan">
    <option repeat.for="clan of clans" model.bind="clan.name">${clan.name}</option>
  </select>
</template>

Notice the value.bind and repeat.for, which should be familiar if you read my earlier column on data binding with Aurelia. The options within the select element bind to the clan model defined in clans.js and then display the clan name. Because my Clans object is simple, with only a name and code (which is extraneous in this example), I could simply use value.bind there. But I’m sticking with using model.bind because it’s a better pattern for me to remember.

The dropdown.js module wires up the markup with the clans, as shown in Figure 6.

Figure 6 Custom Element Code for the Model in Dropdown.js

import $ from 'jquery';
import {getClans} from '../clans';
import {bindable} from 'aurelia-framework';
export class Dropdown {
  @bindable selectedClan;
  attached() {
    $(this.dropdown).dropdown();
  }
  constructor() {
    this.clans = getClans();
  }
}

Additionally, the custom element makes it possible for me to use much simpler markup in my edit view:

<dropdown selected-clan.two-way="ninja.Clan"></dropdown>

Notice that I’m using two Aurelia-specific features here. First, my property is called selectedClan in the view-model, but selected-clan in the markup. The Aurelia convention, based on HTML’s need for attributes to be lowercase, is to make all custom properties of export names lowercase and hyphenated, so it expects that hyphen to be where the camel-cased letters begin. Second, rather than value.bind, I am explicitly using two-way binding here so the clan will be rebound to the ninja when the selection changes.

More important, I can very easily reuse my custom element in other views. In my case, it simply provides more readability and, therefore, more maintainability. But in larger applications, the reuse of the markup and logic is a great benefit.

Pushing Edits Back to DocumentDB

My markup is reading the two pieces of equipment in the graph and displaying them. For the sake of brevity, I’ll leave editing the equipment data to another day.

The last bit of work now is to pass changes back up to the API and send them to DocumentDB. This is triggered with the Save button.

The markup for the Save button uses another Aurelia paradigm, click.delegate—which uses JavaScript event delegation—allowing me to delegate the action to a save function defined in edit.js.

The save function, shown in Figure 7, creates a new object, ninjaRoot, using the relevant properties from the ninja property that was defined in getNinja, which is then bound to the markup, allowing the user to update from the browser.

Figure 7 The ninjas.save Function

save() {
  this.ninjaRoot = {
    Id: this.ninja.id,
    ServedInOniwaban: this.ninja.ServedInOniwaban,
    Clan: this.ninja.Clan,
    Name: this.ninja.Name,
    DateOfBirth: this.ninja.DateOfBirth
  };
  return this.httpClient.fetch('/updateDetails', {
    method: 'post',
    body: json(this.ninjaRoot)
  }).then(response => {this.router.navigate('ninjaList');
  });
}

Save then uses the now-familiar httpClient.fetch to request an API URL called udpateDetails and pass in the ninjaRoot object as the body. Notice, also, that I’m specifying this as a post method, not a get. The API router tells Node.js to route to the updateDetails method of the ninjas module.

router.post('/api/updateDetails', function(request,response){
  ninjas.updateDetails(request,response);
});

Now let’s look at the server-side updateDetails method in ninjas.js:

updateDetails: function (request,response) {
  var self = this;
  var ninja = request.body;
  self.docDbDao.updateItem(ninja, function (err) {
    if (err) {
      throw (err);
    } else {
      response.send(200);
    }
  })
},

I extract the ninjaRoot stored in the request body and set that to a ninja variable, then pass that variable into the controller’s updateItem method. As Figure 8 shows, I’ve modified updateItem a bit since part one of my article to accommodate my ninja type.

Figure 8 The updateItem Method in the docDbDao Controller Uses the Node.js SDK to Talk to DocumentDB

updateItem: function (item, callback) {
  var self = this;
  self.getItem(item.Id, function (err, doc) {
    if (err) {
      callback(err);
    } else {
      doc.Clan=item.Clan;
      doc.Name=item.Name;
      doc.ServedInOniwaban=item.ServedInOniwaban;
      doc.DateOfBirth=item.DateOfBirth;
      doc.DateModified=Date.now();
      self.client.replaceDocument(doc._self, doc, function (err, replaced) {
        if (err) {
          callback(err);
        } else {
          callback(null, replaced);
        }
      });
    }
  });
},

UpdateItem retrieves the document from the database using the id, updates the relevant properties of that document and then uses the SDK DocumentDBClient.replaceDocument method to push the change to my Azure DocumentDB database. A callback alerts me to the operation’s completion. That callback is then noted by the updateDetails method, which returns a 200 response code back to the client module that called the API. If you look back at the client-side save method, you’ll notice that its callback routes to ninjaList. So when the update has been successfully posted, the user is presented with the original listing page of the ninjas. Any edits to the ninja just changed will be visible in that list.

Are We Ninjas Yet?

This solution threw me into an abyss of difficulties and dead ends. While my core goal was to have Aurelia talk to the DocumentDB database, I also wanted to use these technologies to take advantage of their benefits. That meant having to comprehend so many things in the JavaScript world, manage node installations, use Visual Studio Code for the first time because of its ability to debug the Node.js and learn even more about DocumentDB. While there are many other things you may want to do in even a simple sample like mine, this article should give you a basic understanding of how things function from one end to another.

One important point to keep in mind is that DocumentDB—like any NoSQL database—is designed for large volumes of data. It’s not cost-effective for tiny little bits of data such as I use in my sample. But in order to explore the functionality of connecting to the database and interacting with the data, large amounts of data were not required, so five objects sufficed.


Julie Lerman is a Microsoft MVP, .NET mentor and consultant who lives in the hills of Vermont. You can find her presenting on data access and other .NET topics at user groups and conferences around the world. She blogs at thedatafarm.com/blog and is the author of “Programming Entity Framework,” as well as Code First and DbContext editions, all from O’Reilly Media. Follow her on Twitter: @julielerman and see her Pluralsight courses at juliel.me/PS-Videos.

Thanks to the following technical expert for reviewing this article: Patrick Walters
Patrick Walters has been a part of the great community of Aurelia developers who enjoys offering an ear to questions on the official Aurelia gitter channel.