Implementing Client-Side Storage with IndexedDB

An abstract conceptual representation of client-side storage. The image features a computer desktop view with a browser window open. In the browser, different tabs indicate distinct web applications. A visual metaphor of a traditional filing cabinet transformed into a digital format appears, replacing one tab to symbolize IndexedDB. Inside the drawers of the digital cabinet, visualize different types of data like images, documents, music notes, each placed in different compartments. Make sure no brand logos or identifiable labels are visible. Ensure the color scheme is neutral and engaging.

What is IndexedDB and Why Use It?

IndexedDB is a low-level API for client-side storage in web applications, providing more storage capacity and better performance compared to traditional storage options like cookies and localStorage.

IndexedDB is a NoSQL database that allows you to store and retrieve objects that are indexed by key.

The storage capacity available in IndexedDB is significantly larger than other client-side storage options.

This makes it suitable for storing large amounts of structured data.

IndexedDB is transactional, ensuring data integrity even during failures.

It works asynchronously, improving performance by not blocking the main thread.

TLDR: How to Implement IndexedDB in Your Web Application?

Create an IndexedDB database and object stores using the indexedDB.open method and db.createObjectStore.

Add, retrieve, update, and delete data using transactions and requests.

Example code below demonstrates these steps:


// 1. Open/Create a database
const request = indexedDB.open('MyDatabase', 1);

// 2. Create an object store
request.onupgradeneeded = function(event) {
const db = event.target.result;
const objectStore = db.createObjectStore('MyObjectStore', { keyPath: 'id' });
objectStore.createIndex('name', 'name', { unique: true });
};

// 3. Adding data
request.onsuccess = function(event) {
const db = event.target.result;
const transaction = db.transaction(['MyObjectStore'], 'readwrite');
const objectStore = transaction.objectStore('MyObjectStore');
objectStore.add({ id: 1, name: 'John Doe' });
objectStore.add({ id: 2, name: 'Jane Doe' });
};

// 4. Retrieving data
const getRequest = store.get(1);
getRequest.onsuccess = function(event) {
console.log('Data:', event.target.result);
};

// 5. Updating data
const updateRequest = store.put({ id: 1, name: 'John Smith' });
updateRequest.onsuccess = function(event) {
console.log('Data updated.');
};

// 6. Deleting data
const deleteRequest = store.delete(1);
deleteRequest.onsuccess = function(event) {
console.log('Data deleted.');
};

How to Create and Open an IndexedDB Database?

Creating and opening an IndexedDB database starts with the indexedDB.open method.

This method requires the name of the database and its version.

The database version helps in handling schema changes using the onupgradeneeded event.

Here is an example:


// Open or create the database
const request = indexedDB.open('MyDatabase', 1);

// Handle errors
request.onerror = function(event) {
console.error('Database error:', event.target.error);
};

// Handle successful creation/upgradation
request.onsuccess = function(event) {
console.log('Database opened successfully.');
};

// Handle schema changes
request.onupgradeneeded = function(event) {
const db = event.target.result;
const objectStore = db.createObjectStore('MyObjectStore', { keyPath: 'id' });
objectStore.createIndex('name', 'name', { unique: true });
};

How to Create Object Stores and Indexes?

Object stores in IndexedDB are equivalent to tables in a relational database.

You create object stores within the onupgradeneeded event handler.

Indexes are created on object stores for querying data efficiently.

Example code to create object stores and indexes:


request.onupgradeneeded = function(event) {
const db = event.target.result;
const objectStore = db.createObjectStore('MyObjectStore', { keyPath: 'id' });
objectStore.createIndex('name', 'name', { unique: true });
};

How to Add and Retrieve Data?

Adding and retrieving data in IndexedDB requires initiating a transaction.

You specify the object store and transaction mode (read-only or readwrite) within the transaction.

Data is added using the add method, and retrieved using the get method.

Example code to add and retrieve data:


request.onsuccess = function(event) {
const db = event.target.result;

// Adding data
const transaction = db.transaction(['MyObjectStore'], 'readwrite');
const objectStore = transaction.objectStore('MyObjectStore');
objectStore.add({ id: 1, name: 'John Doe' });

// Retrieving data
const getRequest = objectStore.get(1);
getRequest.onsuccess = function(event) {
console.log('Data retrieved:', event.target.result);
};
};

How to Update and Delete Data?

Updating and deleting data in IndexedDB also requires transactions.

Use the put method to update data and the delete method to remove data.

Example code to update and delete data:


request.onsuccess = function(event) {
const db = event.target.result;

// Updating data
const transaction = db.transaction(['MyObjectStore'], 'readwrite');
const objectStore = transaction.objectStore('MyObjectStore');
objectStore.put({ id: 1, name: 'John Smith' });

// Deleting data
const deleteRequest = objectStore.delete(1);
deleteRequest.onsuccess = function(event) {
console.log('Data deleted.');
};
};

Handling Transactions in IndexedDB

Transactions in IndexedDB are used to interact with object stores and indexes.

They ensure data integrity by being atomic, consistent, isolated, and durable (ACID).

Transactions are created using the db.transaction method.

Example code to handle transactions:


const transaction = db.transaction(['MyObjectStore'], 'readwrite');

// Access object store
const objectStore = transaction.objectStore('MyObjectStore');

// Perform operations
objectStore.add({ id: 1, name: 'John Doe' });

transaction.oncomplete = function(event) {
console.log('Transaction completed successfully.');
};

transaction.onerror = function(event) {
console.log('Transaction failed:', event.target.error);
};

IndexedDB Pros and Cons

Pros of IndexedDB:

  • IndexedDB provides large storage capacity.
  • It supports complex queries using indexes.
  • It works asynchronously, thereby not blocking the UI thread.
  • Transactions in IndexedDB ensure data integrity.

Cons of IndexedDB:

  • IndexedDB has a steep learning curve compared to localStorage.
  • Support for IndexedDB varies across browsers.
  • Debugging can be challenging due to its asynchronous nature.
  • It requires more boilerplate code compared to localStorage and cookies.

Comparison with Other Client-Side Storage Options

Let’s compare IndexedDB with localStorage and sessionStorage.

localStorage:

  • localStorage offers synchronous storage, potentially blocking the main thread.
  • It’s limited to about 5 MB per origin.
  • localStorage is easy to use with simple key-value pairs.

sessionStorage:

  • sessionStorage is similar to localStorage but data persists only for the session.
  • It’s useful for temporary storage that shouldn’t persist beyond the tab session.
  • sessionStorage also offers about 5 MB per origin.

IndexedDB:

  • IndexedDB offers asynchronous storage, hence doesn’t block the UI thread.
  • It supports more extensive data storage compared to localStorage and sessionStorage.
  • It’s suitable for storing complex data structures and performing advanced queries.

IndexedDB Best Practices

Follow these best practices to make the most of IndexedDB:

  • Always check for browser compatibility before using IndexedDB.
  • Handle errors gracefully to ensure robust applications.
  • Use indexes efficiently to improve query performance.
  • Structure your database schema wisely for better data management.

Frequently Asked Questions (FAQs)

What browsers support IndexedDB?

IndexedDB is supported by major browsers like Chrome, Firefox, Safari, Edge, and Opera.

What is the maximum storage limit for IndexedDB?

The storage limit varies by browser but is generally much larger than localStorage, around 50 MB to several GB.

Can I use IndexedDB with JavaScript frameworks?

Yes. IndexedDB can be used with frameworks like React, Angular, and Vue.js.

How do I delete an entire IndexedDB database?

Use the indexedDB.deleteDatabase('DatabaseName') method to delete the database.

Is IndexedDB secure?

IndexedDB is subject to the same-origin policy and operates in a secure context (HTTPS).

Can IndexedDB be used in server-side applications?

No. IndexedDB is designed for client-side storage in web applications.

What are object stores and indexes in IndexedDB?

Object stores are like tables in relational databases where data is stored. Indexes are used to query data efficiently.

How do transactions work in IndexedDB?

Transactions in IndexedDB are atomic, ensuring data integrity during read-write operations.

Can I store large files in IndexedDB?

Yes. IndexedDB can handle large data objects but consider the storage limits of different browsers.

What are the alternatives to IndexedDB for client-side storage?

Alternatives include localStorage, sessionStorage, WebSQL (deprecated), and cookies.

What scenarios are best suited for IndexedDB?

IndexedDB is ideal for applications requiring offline support, large data storage, and complex querying.

How to Iterate Over Records in IndexedDB?

When interacting with IndexedDB, you might need to iterate over multiple records.

The openCursor method on an object store or index initiates a cursor to traverse the records.

This approach is useful for reading, updating, or deleting multiple entries at once.

Example code to iterate over records:


request.onsuccess = function(event) {
const db = event.target.result;
const transaction = db.transaction(['MyObjectStore'], 'readonly');
const objectStore = transaction.objectStore('MyObjectStore');
const cursorRequest = objectStore.openCursor();

cursorRequest.onsuccess = function(event) {
const cursor = event.target.result;
if (cursor) {
console.log('Record:', cursor.value);
cursor.continue();
} else {
console.log('All records have been read.');
}
};

cursorRequest.onerror = function(event) {
console.error('Cursor request failed:', event.target.error);
};
};

How to Handle IndexedDB Version Upgrades?

Handling database version upgrades in IndexedDB is essential for managing schema changes.

The onupgradeneeded event is triggered when opening a database with a higher version number.

This event is used to create or modify object stores and indexes.

Example code for handling version upgrades:


// Open the database with a higher version number
const request = indexedDB.open('MyDatabase', 2);

request.onupgradeneeded = function(event) {
const db = event.target.result;

// Check if the object store already exists
if (!db.objectStoreNames.contains('MyObjectStore')) {
const objectStore = db.createObjectStore('MyObjectStore', { keyPath: 'id' });
objectStore.createIndex('name', 'name', { unique: true });
}

// Add or modify indexes as needed
const objectStore = event.target.transaction.objectStore('MyObjectStore');
if (!objectStore.indexNames.contains('email')) {
objectStore.createIndex('email', 'email', { unique: true });
}
};

// Handle errors
request.onerror = function(event) {
console.error('Database error:', event.target.error);
};

request.onsuccess = function(event) {
console.log('Database version upgraded successfully.');
};

How to Index Data for Efficient Queries?

Indexes in IndexedDB allow for efficient querying of data.

Indexes can be created on object stores during the onupgradeneeded event.

Example code to create and use indexes for queries:


// Creating indexes during the onupgradeneeded event
request.onupgradeneeded = function(event) {
const db = event.target.result;
const objectStore = db.createObjectStore('MyObjectStore', { keyPath: 'id' });
objectStore.createIndex('name', 'name', { unique: false });
objectStore.createIndex('email', 'email', { unique: true });
};

// Querying data using indexes
request.onsuccess = function(event) {
const db = event.target.result;
const transaction = db.transaction(['MyObjectStore'], 'readonly');
const objectStore = transaction.objectStore('MyObjectStore');
const index = objectStore.index('email');

// Retrieve data using the index
const indexRequest = index.get('email@example.com');
indexRequest.onsuccess = function(event) {
console.log('Data retrieved using index:', event.target.result);
};
};

Handling IndexedDB Errors

Handling errors gracefully is crucial for robust IndexedDB applications.

Error events can be captured using onerror handlers on various database operations.

Example code for handling IndexedDB errors:


const request = indexedDB.open('MyDatabase', 1);

request.onerror = function(event) {
console.error('Database error:', event.target.error);
};

request.onupgradeneeded = function(event) {
const db = event.target.result;
db.onerror = function(event) {
console.error('Upgrade error:', event.target.error);
};
};

request.onsuccess = function(event) {
const db = event.target.result;
const transaction = db.transaction(['MyObjectStore'], 'readwrite');

transaction.onerror = function(event) {
console.error('Transaction error:', event.target.error);
};

const objectStore = transaction.objectStore('MyObjectStore');
const addRequest = objectStore.add({ id: 1, name: 'John Doe' });

addRequest.onerror = function(event) {
console.error('Add request error:', event.target.error);
};
};

Security Considerations

While IndexedDB provides robust client-side storage, security considerations are essential.

IndexedDB abides by the same-origin policy and hence cannot be accessed across different domains.

Ensure secure contexts (HTTPS) are employed to use IndexedDB.

Always validate and sanitize data before storing or retrieving from IndexedDB.

How to Delete an IndexedDB Database?

Deleting an IndexedDB database is straightforward using the indexedDB.deleteDatabase method.

This method takes the database name as an argument and removes the entire database.

Example code to delete an IndexedDB database:


const deleteRequest = indexedDB.deleteDatabase('MyDatabase');

deleteRequest.onsuccess = function(event) {
console.log('Database deleted successfully.');
};

deleteRequest.onerror = function(event) {
console.error('Database deletion error:', event.target.error);
};

Frequently Asked Questions (FAQs)

What browsers support IndexedDB?

IndexedDB is supported by major browsers like Chrome, Firefox, Safari, Edge, and Opera.

What is the maximum storage limit for IndexedDB?

The storage limit varies by browser but is generally much larger than localStorage, around 50 MB to several GB.

Can I use IndexedDB with JavaScript frameworks?

Yes. IndexedDB can be used with frameworks like React, Angular, and Vue.js.

How do I delete an entire IndexedDB database?

Use the indexedDB.deleteDatabase('DatabaseName') method to delete the database.

Is IndexedDB secure?

IndexedDB is subject to the same-origin policy and operates in a secure context (HTTPS).

Can IndexedDB be used in server-side applications?

No. IndexedDB is designed for client-side storage in web applications.

What are object stores and indexes in IndexedDB?

Object stores are like tables in relational databases where data is stored. Indexes are used to query data efficiently.

How do transactions work in IndexedDB?

Transactions in IndexedDB are atomic, ensuring data integrity during read-write operations.

Can I store large files in IndexedDB?

Yes. IndexedDB can handle large data objects but consider the storage limits of different browsers.

What are the alternatives to IndexedDB for client-side storage?

Alternatives include localStorage, sessionStorage, WebSQL (deprecated), and cookies.

What scenarios are best suited for IndexedDB?

IndexedDB is ideal for applications requiring offline support, large data storage, and complex querying.

Shop more on Amazon