Channels ▼
RSS

Web Development

Understanding Client-Side Storage in Web Apps


IndexedDB

The IndexedDB API is a more capable but far more complex API. The API allows you to store large amounts of structured data (in the form of objects and object stores) and then perform queries on that data using indexes. Most of the API calls happen in transactions, and all requests to set or fetch data are asynchronous.

The IndexedDB is actually the second attempt at a local database API for websites. The first attempt was the WebSQL API, introduced by Apple and supported by Chrome and Opera. Web developers liked that WebSQL used SQL, a common query language for databases; and browser vendors disliked that there are so many variants of SQL with no standard specification; so it was hard to know how to implement it properly. The IndexedDB API, on the other hand, was created from scratch for purposes of Web storage (by Oracle) and quickly gained support from more browser vendors than WebSQL.

In the following example code, I create a database to store our favorites. I then create an object store for favorite fish and add several objects to that store. Finally, I query over that store and display what's in it.

Note that I am using API calls prefixed with "webkit" here — the spec is still not finalized and this is the code that works in Chrome (at the time of writing).

function errorHandler(e) {
   console.log(e);
}

function createDB() {
    var openRequest = webkitIndexedDB.open('Favorites', '2');
    openRequest.onerror = errorHandler;
    openRequest.onsuccess = function(e) {
      db = openRequest.result;
      createStore(db);
    }
}
    
function createStore(db) {
    var versionRequest = db.setVersion('2');
    versionRequest.onerror = errorHandler;
    versionRequest.onsuccess = function(e) {
      if (!db.objectStoreNames.contains('Fish')) {
        var store = db.createObjectStore('Fish');
      }
      populateStore(db);
      queryStore(db);
    }
}

function populateStore(db) {
  var transaction = db.transaction('Fish', webkitIDBTransaction.READ_WRITE);
  var store = transaction.objectStore('Fish');
  store.clear();
  store.put({name: 'Herring', color: 'Silvery'}, 'herring');
  store.put({name: 'Tuna', color: 'Pink'}, 'tuna');
}
        
function queryStore(db) {
  var transaction = db.transaction('Fish', webkitIDBTransaction.READ_ONLY);
  var store = transaction.objectStore('Fish');
  var cursorRequest = store.openCursor();
  cursorRequest.onerror = errorHandler;
  cursorRequest.onsuccess = function(e) {
     var cursor = cursorRequest.result;
     if (cursor) {
       document.getElementById(‘fish’).innerHTML += cursor.value.name + ' is ' + 
	      cursor.value.color + '<br>';
       cursor.continue();
     }
  };
}
    
createDB();

The File API

The File API is a way for websites to store files on the user's file system, scoped to their own little sandbox. The File API is very similar to the IndexedDB in actual capabilities — synchronous data storage and retrieval — but its API may feel more intuitive for developers that are accustomed to dealing with the file system. In addition, the File API specification includes support for storing binary files, such as images or PDFs.

In the example code below, I fetch a local image, save it in the file system as a binary file type, and then retrieve it back and display it on the page.

Note that like the IndexedDB API, I’m using method calls prefixed with "webkit"; again, the specification is not yet finalized and this code is guaranteed to work only in Chrome (at the time of writing).

function fetchImage() {
    var xhr = new XMLHttpRequest();
    xhr.open('GET', 'tunafish.png', true);
    xhr.responseType = 'arraybuffer';
    
    xhr.onload = function(e) {        
      saveImage(xhr.response);
    };
    
    xhr.send();
}

function saveImage(response) {
    window.webkitRequestFileSystem(TEMPORARY, 1024 * 1024, function(fs) {
        fs.root.getFile('favfish.png', {create: true}, function(fileEntry) {
          fileEntry.createWriter(function(writer) {
            var bb = new WebKitBlobBuilder();
            bb.append(response);
            writer.write(bb.getBlob('image/png'));
            readImage(fs);        
          }, onError);
        }, onError);
    }, onError);  
}

function readImage(fs) {
  fs.root.getFile('favfish.png', {}, function(fileEntry) {
    fileEntry.file(function(file) {
       var reader = new FileReader();
       reader.onloadend = function(e) {
         document.getElementById(‘fish’).innerHTML += ‘<img src="' + 
              this.result + '">';
       };
       reader.readAsDataURL(file);
    }, onError);
  }, onError);
}

function onError(e) {
  console.log('Error', e);
}
                         
fetchImage();

Which API to Use?

As you've seen, HTML5 gives Web developers several options for client-side storage, each with their own distinct APIs and optimal use cases. When you're deciding which to use, there are a few questions you should ask about the APIs and your own needs. Let's go through them and see how each API stacks up — and because cookies are still a reasonable option for some uses, I will include cookies in the comparisons.

What Kind of Data Can You Store?

The APIs differ in what kind of data they can store, so depending on what you need to store for your app, you may be limited in your storage options.

Cookies and localStorage accept only strings, but of course, you can often serialize other types of data into strings (via JSON, for example); so if it is not binary, it can probably be turned into a string. IndexedDB can natively accept most JavaScript objects (with a few exceptions, such as functions). The File API accepts both text and binary objects, so it is the most capable in this regard.

This table summarizes the storage types:

Storage API Data Type
Cookies String
localStorage Strings
IndexedDB Most JS Objects
File API Text, Binary


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 
Dr. Dobb's TV