IndexedDB is a low-level API for client-side storage of significant amounts of structured data, including files/blobs. It’s a powerful way to store and retrieve data in a user’s browser, offering more storage capacity than localStorage and providing asynchronous access to data.
This guide will walk you through the basics of using IndexedDB, including how to perform Create, Read, Update, and Delete (CRUD) operations.
Table of Contents
7. Conclusion
Introduction to IndexedDB
What is IndexedDB?
IndexedDB is a NoSQL database built into modern browsers, allowing you to store significant amounts of data on the client side. Unlike localStorage, which stores data as simple key-value pairs and is synchronous, IndexedDB stores data in object stores and operates asynchronously, preventing UI blocking.
Why Use IndexedDB?
- Storage Capacity: IndexedDB can store much more data than localStorage.
- Structured Data: It allows storing complex objects.
- Asynchronous: Non-blocking operations enhance performance.
- Transactions: Provides ACID-compliant transactions for data integrity.
- Indexes: Supports indexing to improve query performance.
Browser Support
IndexedDB is supported in all modern browsers:
- Chrome
- Firefox
- Safari
- Edge
- Opera
Basic Concepts
Before diving into code, it’s essential to understand some fundamental concepts:
- Database: The top-level container. You open a connection to a database.
- Object Store: Similar to a table in SQL databases, it holds records.
- Record: An object representing the data you store.
- Key: A unique identifier for a record within an object store.
- Transaction: All read/write operations occur within transactions.
- Index: Allows you to query data efficiently based on properties.
Setting Up IndexedDB
Opening a Database
To interact with IndexedDB, you first need to open a connection to the database.
let db;
const request = indexedDB.open('MyTestDatabase', 1);
request.onupgradeneeded = function(event) {
db = event.target.result;
// Create an object store
const objectStore = db.createObjectStore('customers', { keyPath: 'id', autoIncrement: true });
// Create an index to search customers by name
objectStore.createIndex('name', 'name', { unique: false });
};
request.onsuccess = function(event) {
db = event.target.result;
console.log('Database opened successfully');
};
request.onerror = function(event) {
console.error('Database error:', event.target.errorCode);
};
Explanation:
- indexedDB.open(): Opens a connection to the database.
- onupgradeneeded: Triggered when the database is created or a higher version number is passed.
- createObjectStore(): Creates an object store (like a table).
- createIndex(): Creates an index on a specified property.
CRUD Operations
Create
You can add data to the object store using the add()
or put()
methods.
function addCustomer(customer) {
const transaction = db.transaction(['customers'], 'readwrite');
const objectStore = transaction.objectStore('customers');
const request = objectStore.add(customer);
request.onsuccess = function() {
console.log('Customer added:', customer);
};
request.onerror = function(event) {
console.error('Add failed:', event.target.errorCode);
};
}
// Usage
addCustomer({ name: 'John Doe', email: '[email protected]' });
Explanation:
- transaction(): Creates a transaction for specified object stores.
- objectStore(): Accesses an object store within the transaction.
- add(): Adds a new record to the object store.
Read
Read a Single Record
function getCustomer(id) {
const transaction = db.transaction(['customers']);
const objectStore = transaction.objectStore('customers');
const request = objectStore.get(id);
request.onsuccess = function(event) {
if (request.result) {
console.log('Customer:', request.result);
} else {
console.log('Customer not found');
}
};
request.onerror = function(event) {
console.error('Get failed:', event.target.errorCode);
};
}
// Usage
getCustomer(1);
Read All Records
function getAllCustomers() {
const transaction = db.transaction(['customers']);
const objectStore = transaction.objectStore('customers');
const request = objectStore.getAll();
request.onsuccess = function(event) {
console.log('All customers:', request.result);
};
request.onerror = function(event) {
console.error('GetAll failed:', event.target.errorCode);
};
}
// Usage
getAllCustomers();
Explanation:
- get(): Retrieves a record by key.
- getAll(): Retrieves all records from the object store.
Using Cursors
Cursors allow you to iterate over records.
function iterateCustomers() {
const transaction = db.transaction(['customers']);
const objectStore = transaction.objectStore('customers');
const request = objectStore.openCursor();
request.onsuccess = function(event) {
const cursor = event.target.result;
if (cursor) {
console.log('Customer:', cursor.value);
cursor.continue();
} else {
console.log('No more entries');
}
};
request.onerror = function(event) {
console.error('Cursor failed:', event.target.errorCode);
};
}
// Usage
iterateCustomers();
Update
To update a record, you can use the put()
method.
function updateCustomer(id, updatedData) {
const transaction = db.transaction(['customers'], 'readwrite');
const objectStore = transaction.objectStore('customers');
const request = objectStore.get(id);
request.onsuccess = function(event) {
const data = request.result;
if (data) {
// Update the fields
Object.assign(data, updatedData);
const updateRequest = objectStore.put(data);
updateRequest.onsuccess = function() {
console.log('Customer updated:', data);
};
updateRequest.onerror = function(event) {
console.error('Update failed:', event.target.errorCode);
};
} else {
console.log('Customer not found');
}
};
request.onerror = function(event) {
console.error('Get failed:', event.target.errorCode);
};
}
// Usage
updateCustomer(1, { email: '[email protected]' });
Explanation:
- get(): Retrieve the existing record.
- Object.assign(): Merge updated data into the existing record.
- put(): Updates the record.
Delete
To delete a record, use the delete()
method.
function deleteCustomer(id) {
const transaction = db.transaction(['customers'], 'readwrite');
const objectStore = transaction.objectStore('customers');
const request = objectStore.delete(id);
request.onsuccess = function() {
console.log('Customer deleted:', id);
};
request.onerror = function(event) {
console.error('Delete failed:', event.target.errorCode);
};
}
// Usage
deleteCustomer(1);
Explanation:
- delete(): Removes a record by key.
Error Handling
Always include error handlers to catch any issues.
- request.onerror: Handles errors for individual requests.
- transaction.onerror: Handles errors for the entire transaction.
Example:
transaction.onerror = function(event) {
console.error('Transaction failed:', event.target.errorCode);
};
Best Practices
- Versioning: Increment the database version number to trigger
onupgradeneeded
for schema changes. - Indexes: Use indexes to optimize query performance.
- Transactions: Group multiple operations in a single transaction when possible.
- Promises: Consider wrapping IndexedDB calls in Promises for cleaner asynchronous code.
- Error Handling: Always include error handlers for robustness.
- Feature Detection: Check if the browser supports IndexedDB.
Example of Feature Detection:
if (!('indexedDB' in window)) {
console.error('This browser doesn\'t support IndexedDB');
}
Conclusion
IndexedDB is a robust solution for client-side storage of large amounts of structured data. By understanding its core concepts and following best practices, you can effectively implement CRUD operations in your web applications.
References:
Learn More: AI and Machine Learning
How to Run MongoDB in Docker- Step by Step Guide
Automated Log Cleanup for GBase 8a: A Step-by-Step Guide
How to Set Up a Self-Hosted PostgreSQL Database with SSL Support
7 Best Open-Source AI Coding Tools Every Developer Should Know