Web Databases are an HTML5 feature that stores data inside the user's browser rather than on a server. Because they support rich queries, they open the door to applications that keep working when the network does not. The sample below builds a minimal todo list manager and walks through the API at a high level.

Scoping the database logic

The sample wraps all of the database code in a namespace so it doesn't leak into the global scope.

var html5rocks = {};
html5rocks.webdb = {};

How the API behaves

Most code will use the Asynchronous API. It is non-blocking, so results never come back as return values — they arrive at a callback function you supply.

Access through HTML is also transactional: SQL statements cannot run outside a transaction. Two flavors exist, read/write (transaction()) and read-only (readTransaction()). A read/write transaction locks the entire database.

Opening and populating the store

Nothing can be read or written until the database has been opened, which requires a name, version, description and size.

html5rocks.webdb.db = null;

html5rocks.webdb.open = function() {
var dbSize = 5 * 1024 * 1024; // 5MB
html5rocks.webdb.db = openDatabase("Todo", "1", "Todo manager", dbSize);
}

html5rocks.webdb.onError = function(tx, e) {
alert("There has been an error: " + e.message);
}

html5rocks.webdb.onSuccess = function(tx, r) {
// re-render the data.
// loadTodoItems is defined in Step 4a
html5rocks.webdb.getAllTodoItems(loadTodoItems);
}

Tables come into existence only through a CREATE TABLE statement executed inside a transaction. The sample defines a function that runs from the body onload event and creates the table when it is missing.

The table, todo, carries three columns:

  • ID — an incrementing sequential ID column
  • todo — a text column holding the item body
  • added_on — the time the item was created
html5rocks.webdb.createTable = function() {
var db = html5rocks.webdb.db;
db.transaction(function(tx) {
tx.executeSql("CREATE TABLE IF NOT EXISTS " +
                "todo(ID INTEGER PRIMARY KEY ASC, todo TEXT, added_on DATETIME)", []);
});
}

Inserting items

Adding an item means creating a transaction, then issuing an INSERT against the todo table. executeSql accepts the SQL to run plus the parameter values to bind to the query.

html5rocks.webdb.addTodo = function(todoText) {
var db = html5rocks.webdb.db;
db.transaction(function(tx){
var addedOn = new Date();
tx.executeSql("INSERT INTO todo(todo, added_on) VALUES (?,?)",
    [todoText, addedOn],
    html5rocks.webdb.onSuccess,
    html5rocks.webdb.onError);
});
}

Reading items back

Chrome's web database implementation uses standard SQLite SELECT queries to retrieve stored rows.

html5rocks.webdb.getAllTodoItems = function(renderFunc) {
var db = html5rocks.webdb.db;
db.transaction(function(tx) {
tx.executeSql("SELECT * FROM todo", [], renderFunc,
    html5rocks.webdb.onError);
});
}

Every call here is asynchronous, so neither the transaction nor executeSql returns data. Results are handed to the success callback instead.

That callback is loadTodoItems. It receives two arguments — the transaction of the query and the result set — and iterates over the rows:

function loadTodoItems(tx, rs) {
var rowOutput = "";
var todoItems = document.getElementById("todoItems");
for (var i=0; i < rs.rows.length; i++) {
rowOutput += renderTodo(rs.rows.item(i));
}

todoItems.innerHTML = rowOutput;
}
function renderTodo(row) {
return "<li>" + row.todo + 
        " [<a href='javascript:void(0);' onclick=\'html5rocks.webdb.deleteTodo(" + 
        row.ID +");\'>Delete</a>]</li>";
}

Rendering lands the list inside a DOM element named todoItems.

Removing items

html5rocks.webdb.deleteTodo = function(id) {
var db = html5rocks.webdb.db;
db.transaction(function(tx){
tx.executeSql("DELETE FROM todo WHERE ID=?", [id],
    html5rocks.webdb.onSuccess,
    html5rocks.webdb.onError);
});
}

Wiring the page together

On page load the app opens the database, creates the table if needed, and renders whatever todo items are already stored.

....
function init() {
html5rocks.webdb.open();
html5rocks.webdb.createTable();
html5rocks.webdb.getAllTodoItems(loadTodoItems);
}
</script>

<body onload="init();">

Getting data out of the DOM requires a helper that calls html5rocks.webdb.addTodo:

function addTodo() {
var todo = document.getElementById("todo");
html5rocks.webdb.addTodo(todo.value);
todo.value = "";
}