Cross Posted from a Script Junkie Article
If you use your web apps as much as your desktop applications, I am sure you hate the part where you have to be online to get them to work. Well, the HTML5 set of standards may have a solution. With the ability to make disconnect applications possible, this is yet another boundary that been blurred between the web and native applications. Among other things, the “capability-hungry” web applications have started asking for the equivalent of a database on the browser and the IndexedDB standard promises to fill that gap. Whether it is to let you work while disconnected, or simply to load you application faster by caching resources locally, offline support is an important aspect of the next generation of web applications.
IndexedDB would be used by different classes of applications like
- Intermittently connected apps that should run in the offline mode also. Examples include mail and calendar clients that are synced when the connection is restored.
- Disconnected applications that do not require a server. “To Do” Lists or Sticky Notes are an example of this.
- Applications with large amounts of data may prefer to cache it at the client, using the database. This could be any regular website that can store user profile or shopping catalogs on the browser.
Exploring the (other) Options
Cookies were the way to store user data in the good old days, but that was for the last decade. Cookies are tiny and just not powerful enough. LocalStorage is promising, but the powerful web applications of today need a real database as a storage option. The WebDatabase API seemed to be the answer, but as Mozilla points out, standardizing SQL may not be easy. A simpler storage API based on indexed sequential access mode (ISAM) is the basis of the IndexedDB API.Hello IndexedDB
Simply put, IndexedDB is a persistent store that lets you save and retrieve JavaScript objects. The objects are indexed based on a “key” or an “index” property of the object. The API also allows for iterating over the objects. Hence, IndexedDB is not really relational in the sense of a traditional database; relationships between different object stores are not explicit. The storage is also subject to the same origin policy.The IndexedDB APIs are available in two flavors –
- The Synchronous APIs, available with WebWorkers, wait for the database operation to complete and then return with the result of the operation.
- The Asynchronous API, available on the global window object, does not wait for the database operation to complete and returns a “request” immediately. The onsuccess or onerror event handlers on the request are executed once the database operations are complete with the status of the operation usually is in the “result” property of the event.
Browser Support Today
The IndexedDB specification is still a working draft, but the major browser vendors have released implementations to let end users play with the API and provide feedback on the API. IndexedDB support is available in- Internet Explorer using a prototype implementation
- Firefox 4.0 Beta 10+
- Google Chrome 8.0.522 (dev) and later
- No support on Opera or Safari
More browser specific information is available at http://caniuse.com/indexeddb.
The Database [IE] [FF] [CH]
With the above mentioned properties initialized, the object store is ready to be used. The first step is to open the database, using the open method. The code is self-explanatory.
var request = window.indexedDB.open("DatabaseName", "DatabaseDescription");
request.onsuccess = function(event){
var database = event.result;
write("Database Opened", database);
};
request.onerror = function(e){
writeError(e);
};
A Store of Objects [IE] [FF] [CH]
The operations like get or put in IndexedDB are transactional. These operations can be in one of the transaction scopes (read only, read-write, snapshot read or version change) that follow simple rules to prevent operations from overstepping each other.Without going into theory about transactions, an Object Store (logical equivalent of a table in the relational world) can to be created to hold individual objects.
Note that a new object store can only be created or deleted in the “version change” transaction scope.
var request = db.setVersion(1); request.onsuccess = function(e){ write("Version changed to ", db.version, ", so trying to create a database now."); var objectStore = db.createObjectStore("BookList", "id", true); };
An existing object store can be opened using a transaction created in the required scope. The last line in the example below returns the object store.
var transaction = db.transaction(["BookList"], 0 /*Read-Write*/, 1000 /*Time out in ms*/);
transaction.oncomplete = function(e){write("===== Transaction Complete");};
var objectStore = transaction.objectStore("BookList");
Put it in the sack, and get it back [IE] [FF] [CH]
The Object Store provides methods to fetch or save objects across browser sessions. As in this case, auto-generated keys are should not be a part of the object to be saved.
var data = {"bookName" : "Name", "price" : 100, "rating":"good"};
var request = objectStore.add(data);
request.onsuccess = function(event){
document.write("Saved with id ", event.result)
var key = event.result;
};
var request = objectStore.get(key);
request.onsuccess = function(event){
document.write("Got data ", event.result)
};
Let’s go over it [IE] [FF] [CH]
A “cursor” lets you iterate over the objects in the store. After opening the cursor, a continue method is called repeatedly for the iteration.
keyRange = new IDBKeyRange.bound(1, 10, true, false);
var request = DAO.objectStore.openCursor(keyRange);
request.onsuccess = function(event){
var cursor = event.result;
write(cursor.key , cursor.value); // cursor.key is the object key, cursor.value is the actual object.
cursor["continue"]();
};
“Index” path in IndexedDB [IE] [FF] [CH]
The objects can also be iterated over non-key properties of the object. An “index” (like a secondary key) needs to be created over the required properties and can only be done in the version change transaction scope.
var index = objectStore.createIndex("priceIndex", "price", true);
var index = objectStore.index("priceIndex");
var keyCursor = index.openKeyCursor(); //key=index property, value = primary key
var objectCursor = index.openCursor(); //key=index property, value = object
Looking ahead
The examples above illustrate the most basic examples to open a database, save and fetch objects, and iterate over them. Without the complexity of SQL, the IndexedDB APIs are powerful enough to allow real world applications to use database capabilities in the browser. On a side note, many have voiced concerns over the lack of familiar querying capabilities with abilities to perform better filtering or “table joins”. JavaScript libraries may be built on top of this API for this functionality.Though it may not be ready for production yet due to lack “deployed” browser support, this is one capability you application can look forward to.