I’ve been reading recently about the new HTML5 specs, and one thing that really interested me was the Web SQL Database spec that would allow javascript developers to access a client-side database from within the browser, in order to save and manipulate data locally on the users machine. This would enable interactive javascript web applications to run offline, in a similar manner to how Google Gears works (think storing emails for access at a time when no internet connection is available). The WebDB API defined a relational database that could be queried using SQL. Unfortunately, as of 18th November 2010, this spec was canned, because it suffered from one fatal flaw: it used SQL. The problem was that in order to define a cross-browser compatible API, all vendors would need to implement the same database (or more specifically, the same form of SQL). The spec pushed SQLLite as the database implementation, which both Chrome and Safari agreed with, but since Microsoft wanted to use a version of SQL Server in IE, work on the specification was ceased.

The replacement for the WebDB API was the Indexed Database API. Unlike webDB, this was a NoSQL database implementation, which used object stores rather than the typical relational database implementation. The main problem with the IndexedDB API, it seemed to me, however, was that the syntax was just awful compared to it’s SQL alternative. The code samples found at Mozilla Hacks¬†show this pretty well (although it seems that the point of the post is supposed to sing the advantages of IndexedDB over WebDB!). This example, taken from that post, shows the different code samples required to load and display all the kids in a database that have bought candy:

WebDB

var db = window.openDatabase("CandyDB", "1",
                             "My candy store database",
                             1024);
db.readTransaction(function(tx) {
  tx.executeSql("SELECT name, COUNT(candySales.kidId) " +
                "FROM kids " +
                "LEFT JOIN candySales " +
                "ON kids.id = candySales.kidId " +
                "GROUP BY kids.id;",
                function(tx, results) {
    var display = document.getElementById("purchaseList");
    var rows = results.rows;
    for (var index = 0; index < rows.length; index++) {
      var item = rows.item(index);
      display.textContent += ", " + item.name + "bought " +
                             item.count + "pieces";
    }
  });
});

IndexedDB

candyEaters = [];
function displayCandyEaters(event) {
  var display = document.getElementById("purchaseList");
  for (var i in candyEaters) {
    display.textContent += ", " + candyEaters[i].name + "bought " +
                           candyEaters[i].count + "pieces";
  }
};

var request = window.indexedDB.open("CandyDB",
                                    "My candy store database");
request.onsuccess = function(event) {
  var db = event.result;
  var transaction = db.transaction(["kids", "candySales"]);
  transaction.oncomplete = displayCandyEaters;

  var kidCursor;
  var saleCursor;
  var salesLoaded = false;
  var count;

  var kidsStore = transaction.objectStore("kids");
  kidsStore.openCursor().onsuccess = function(event) {
    kidCursor = event.result;
    count = 0;
    attemptWalk();
  }
  var salesStore = transaction.objectStore("candySales");
  var kidIndex = salesStore.index("kidId");
  kidIndex.openObjectCursor().onsuccess = function(event) {
    saleCursor = event.result;
    salesLoaded = true;
    attemptWalk();
  }
  function attemptWalk() {
    if (!kidCursor || !salesLoaded)
      return;

    if (saleCursor && kidCursor.value.id == saleCursor.kidId) {
      count++;
      saleCursor.continue();
    }
    else {
      candyEaters.push({ name: kidCursor.value.name, count: count });
      kidCursor.continue();
    }
  }
}

Pretty monstrous right? Which is a real shame, since IndexedDB has the potential to be a really, really useful in a client side developers toolkit.