IndexedDB – A Web API for Client-side Storage

What is IndexedDB?

IndexedDB is an API for storing significant amounts of structured data in a user’s browser. It helps to do high-performance searches using indexes.

The data stored in IndexedDB is persistent and work online and offline. It provides both a synchronous and asynchronous API. In practice, however, all current implementations are asynchronous, and requests will not block the user interface from loading.

Like most web storage solutions, IndexedDB follows a same-origin policy. So while you can access stored data within a domain, you cannot access data across different domains. There is some other traditional way to store data in the browser, though broadly used ones are cookies. There are limitations and drawbacks when using cookies that are limited to about 4 KB of data. Cookies are sent along with every HTTP Request, it slows down your application by sending the same data over and over needlessly. Most browsers allow only 20 cookies per site if you try to store more, the oldest cookies are discarded. Because cookies have been used to spy on people’s surfing behavior, security-conscious people and companies turn them off or request to be asked every time whether a cookie should be set.

Another broadly used option is Local Storage. local-storage, or more accurately DOM Storage, was designed for smaller amounts of data. It’s essentially string-only key-value storage, with a simplistic synchronous API. We cannot store JavaScript objects directly in local storage. We need to convert the objects into a string to store. So direct querying is not possible in local storage.

Unlike cookies and DOM Storage, IndexedDB provides features that enable you to group, iterate, search, and filter JavaScript objects. IndexedDB is a superior solution for offline storage in browsers when we handle a large amount of structured data. But compared to DOM Storage, its API is quite complicated. There are some polyfills and wrappers like local forage and dexie.js to make it simple. local forage is a polyfill that uses IndexedDB in the background, has some fallback mechanism to WebSQL, and then to local storage in browsers when indexedDB is not supported. dexie.js is a wrapper for IndexedDB that allows much faster code development via simple syntax.

IndexedDB is a transactional database system, while it is also a javascript object-oriented database where data is stored in the form of javascript objects. Objects are grouped into object stores, and these objects are indexed with a key. You can run basic queries on your database and fetch records by looking up their keys in specific key ranges as we can also store images and files in IndexedDB.

Getting Started with IndexedDB API

Here we are going to discuss IndexedDB operations with an example that I have uploaded to GitHub.

i. Create a Database and Object Store with Indexes

In the following example, we are going to create a database and employee object store which holds employee details by their “EmployeeID” attribute. Additionally “name” and “designation” attributes make indexes. “name” attributes are indexed with unique constraints, so it does not allow the same name to repeat (case sensitive). Indexes are used for a lookup table.

A connection to the database is opened. If the “inapp” database did not already exist, it is created and an event handler creates the object store and indexes.

var request = indexedDB.open(“inapp”);

request.onupgradeneeded = function() {

// The database did not previously exist, so create object stores and indexes.

db = request.result;

var store = db.createObjectStore(“employees”, {keyPath: “empid”});

var nameIndex = store.createIndex(“by_name”, “name”, {unique: true});

var designationIndex = store.createIndex(“by_designation”, “designation”);

// Populate with initial data.

store.put({name: “Mohammed Safeer”, designation: “Software Engineer”, empid: “1”});

store.put({name: “Damodaran”, designation: “System Analyst”, empid: “2”});

store.put({name: “Ratheesh Kumar”, designation: “Software Engineer”, empid: “3”});

};

ii. Add data to Object Store

The following example populates the database using a transaction.

var tx = db.transaction(“employees”, “readwrite”);

var store = tx.objectStore(“employees”);

var request = store.put({empid: “4”, name: “John”, designation: “Business Analyst”});

request.onsuccess = function(){

// data added successfully

}

request.onerror = function() {

// The uniqueness constraint of the “by_name” index failed.

console.log(request.error);

// Could call request.preventDefault() to prevent the transaction from aborting.

};

tx.onabort = function() {

// Otherwise the transaction will automatically abort due the failed request.

console.log(tx.error);

};

iii. Update data to Object Store

The following example updates the entry with employee id 4 using a transaction. The update is the same as Insert, here we use an existing Key Path value (here empid 4 in this example).

var tx = db.transaction(“employees”, “readwrite”);

var store = tx.objectStore(“employees”);

var request = store.put({empid: “4”, name: “James”, designation: “Marketing Manager”});

iv. Search Object Store with index and cursor

The following example looks up all employee in the database by name using an index and a cursor. Note that it is case sensitive.

var tx = db.transaction(“employees”, “readonly”);

var store = tx.objectStore(“employees”);

var index = store.index(“by_name”);

var request = index.openCursor(IDBKeyRange.only(“John”));

request.onsuccess = function(e) {

var cursor = e.target.result

if (cursor) {

// Called for each matching record.

cursor.continue();

} else {

// No more matching records.

}

};

v. Retrieve whole employees in Object Store

The following example lists all the employees in the employee objectstore

var tx = db.transaction(“employees”, “readonly”);

var store = tx.objectStore(“employees”);

// Get everything in the store

var keyRange = IDBKeyRange.lowerBound(0);

var request = store.openCursor(keyRange);

// This fires once per row in the store. So, for simplicity,

// collect the data in an array (data), and pass it in the

// callback in one go.

var data = [];

request.onsuccess = function(e) {

var result = e.target.result;

// If there’s data, add it to array

if (result) {

data.push(result.value);

result.continue();

// Reach the end of the data

} else {

callback(data);

}

};

vi. Delete an entry from Object Store with Key Path

In the following example, we delete an entry with empid (Key Path) value 4

var tx = db.transaction(“employees”, “readwrite”);

var store = tx.objectStore(“employees”);

var request = store.delete(“4”);

request.onsuccess = function() {

// do something

};

vii. Clear Object Store

The following example clear object store “employees”

var tx = db.transaction(“employees”, “readwrite”);

var store = tx.objectStore(“employees”);

store.clear();

Have questions? Contact the technology experts at InApp to learn more.