Mastering IndexedDB: A Powerful Way to Store Data in Web Applications

takuya
The Web Tub
Published in
5 min readMay 28, 2024

There are several ways to store data in web applications, but IndexedDB stands out as particularly feature-rich. While Cookies and LocalStorage are relatively well-known, IndexedDB surpasses them in terms of functionality.

In this article, we’ll provide a beginner-friendly explanation of the basic usage and practical applications of IndexedDB.

IndexedDB API — Web APIs | MDN

What is IndexedDB?

As the name suggests, IndexedDB is a type of database. It is a database that runs within a web browser, allowing you to save, retrieve, and delete data. IndexedDB has the following characteristics:

  • Stores data as key-value pairs
  • Enables fast searches by creating indexes
  • Supports transactions to maintain data integrity
  • Allows schema version management
  • Most operations are performed asynchronously

IndexedDB is similar to LocalStorage in terms of being a key-value store (KVS). However, while LocalStorage can only store strings, IndexedDB can store objects as values, which is a significant difference.

Basic Usage of IndexedDB

Now, let’s dive into the basic usage of IndexedDB.

Opening a Database

To use IndexedDB, you first need to open a database using the indexedDB.open() method.

const request = indexedDB.open("myDatabase", 1);

Here, the first argument is the database name, and the second argument is the database version.

You can check whether the database was successfully opened using callbacks.

// On success
request.onsuccess = function(event) {
const db = event.target.result;
console.log("Database opened successfully");
};
// On error
request.onerror = function(event) {
console.log("Failed to open database");
};

Defining Object Stores

Next, you define object stores (similar to tables) within the database. The keyPath specifies the primary key. This process is done inside the request.onupgradeneeded event handler.

request.onupgradeneeded = function(event) {
const db = event.target.result;
const objectStore = db.createObjectStore("customers", { keyPath: "id" });
};

Here, the first argument of the createObjectStore() method is the object store name, and the second argument is an options object. keyPath specifies the property name that will serve as the primary key. You can also define indexes for an object store.

objectStore.createIndex("name", "name", { unique: false });

The request.onupgradeneeded event is triggered when the version number changes.

Adding Data

To add data to an object store, you use a transaction. For example, let’s add the following data:

const customers = [
{
"id": 1,
"name": "Alice",
"age": 20
},
{
"id": 2,
"name": "Bob",
"age": 30
}
]

Then, using a transaction, you can add the data.

The first argument of the transaction method is the name of the object store (as a string or array), and the second argument is the mode. If you need to read and write, specify readwrite, but it can be omitted if you only need to retrieve data.

const customerObjectStore = db
.transaction("customers", "readwrite")
.objectStore("customers");
customerData.forEach((customer) => {
customerObjectStore.add(customer);
});

Searching for Data

To search for data, you also use a transaction.

const transaction = db.transaction(["customers"]);
const objectStore = transaction.objectStore("customers");
const request = objectStore.get("1");
request.onerror = (event) => {
// Error handling
};
request.onsuccess = (event) => {
console.log(`ID for 2 is ${request.result.name}`);
};

Here, the get() method is used to retrieve the data with the primary key “1”.

Updating Data

To update data, you use the put method.

const request = db
.transaction(["customers"], "readwrite")
.objectStore("customers")
.put({ id: 1, name: "Alice", age: 21 });

Deleting Data

To delete data, you start a transaction, access the object store, and use the delete() method.

const request = db
.transaction(["customers"], "readwrite")
.objectStore("customers")
.delete("1");
request.onsuccess = (event) => {
// Callback when deletion is complete
};

This covers the basic usage of IndexedDB.

Practical Applications of IndexedDB

Simplify with Wrapper Libraries

IndexedDB primarily uses asynchronous processing, requiring the definition of event handlers like onsuccess. This might feel a bit complex for beginners. In modern JavaScript, it’s common to use async/await or Promises.

That’s where using an IndexedDB wrapper library comes in handy. In particular, “Dexie.js” is an easy-to-use library that allows you to manipulate IndexedDB with simple code.

Dexie.js is particularly user-friendly. Here’s an example of its code:

const db = new Dexie("MyDatabase");
// Define tables (++id is auto-incrementing)
db.version(1).stores({
friends: "++id, name, age"
});
// Search for data
const oldFriends = await db.friends
.where("age").above(75)
.toArray();
// Add data
await db.friends.add({
name: "Camilla",
age: 25,
street: "East 13:th Street",
picture: await getBlob("camilla.png")
});

Other useful libraries include “PouchDB” and “idb”. By using these, you can master IndexedDB without stress.

Building Offline Applications

One of the major advantages of IndexedDB is that it runs within the browser, eliminating the need for a network connection. This allows you to create applications that work offline.

For example, you can download data from a cloud service in bulk and perform the actual operations using IndexedDB. In this case, it’s important to separate the synchronization process between the cloud and IndexedDB, and the processing between the frontend and IndexedDB. If you can handle that, creating an offline application isn’t difficult.

By decoupling the network and the database, the application can continue functioning even if the cloud service goes down. This leads to an improved user experience.

Combining with Web Workers for High-Speed Processing

IndexedDB becomes even more powerful when combined with Web Workers. Web Workers are JavaScript scripts that run in the background of the browser, allowing you to assign heavy processing tasks to them. For instance, you can register data that needs processing in the frontend to IndexedDB and invoke a Web Worker. The Web Worker then references and updates the data in IndexedDB. Since IndexedDB can also handle binary files, you can even perform image processing using Web Workers.

By combining IndexedDB with Web Workers in this way, you can significantly enhance the performance of your application.

Conclusion

IndexedDB is an extremely useful tool for web developers. By using wrapper libraries, you can simplify its usage; by creating offline applications, you can improve convenience; and by combining it with Web Workers, you can achieve high-speed processing. Make sure to master IndexedDB and create even better applications!

--

--