IndexedDB is a powerful, low-level API for storing large amounts of structured data within the user’s browser, allowing offline access and efficient data management. Unlike localStorage, which stores data as simple key-value pairs, IndexedDB supports storing complex, structured data such as objects and allows for advanced querying. This makes IndexedDB suitable for more complex web applications, like offline-first apps, data-heavy applications, and applications that need to cache data locally.
In this chapter, we’ll cover everything about IndexedDB from the basics to advanced concepts, including creating a database, performing CRUD operations, and understanding transactions. With IndexedDB, web applications can become faster, more responsive, and work effectively even when the user is offline.
IndexedDB is a NoSQL database embedded in web browsers, allowing developers to store complex data like objects, arrays, and binary data. Unlike relational databases that use tables and SQL, IndexedDB uses a system of object stores to store records. Each record in an object store is uniquely identified by a key, making it efficient for querying.
Key Features of IndexedDB:
To create and use an IndexedDB database, you need to open a connection, create an object store (like a table in SQL), and use transactions to interact with the data.
let db;
const request = indexedDB.open("MyDatabase", 1); // Open a database called "MyDatabase"
// Handle database upgrades (like creating object stores)
request.onupgradeneeded = function(event) {
db = event.target.result;
const objectStore = db.createObjectStore("users", { keyPath: "id", autoIncrement: true });
objectStore.createIndex("name", "name", { unique: false });
console.log("Database setup complete.");
};
// Handle successful opening
request.onsuccess = function(event) {
db = event.target.result;
console.log("Database opened successfully.");
};
// Handle errors
request.onerror = function(event) {
console.error("Error opening database:", event.target.errorCode);
};
indexedDB.open("MyDatabase", 1)
: Opens a database named “MyDatabase” with a version of 1.onupgradeneeded
: Fires if the database is being created for the first time or a new version is requested. This is where you create object stores and indexes.keyPath
: Sets a unique identifier (id
) for each entry in the store.Output: When the code runs, it sets up an IndexedDB database called “MyDatabase” with an object store for “users.”
After setting up the database, let’s add some data to it. Adding data in IndexedDB is done through transactions.
function addUser(name, age) {
const transaction = db.transaction(["users"], "readwrite");
const objectStore = transaction.objectStore("users");
const request = objectStore.add({ name: name, age: age });
request.onsuccess = function() {
console.log("User added:", { name, age });
};
request.onerror = function() {
console.error("Error adding user.");
};
}
addUser("Alice", 25);
addUser("Bob", 30);
transaction
: Initiates a transaction with “readwrite” permission.objectStore.add
: Adds an object to the users
store.{ name: name, age: age }
is added to the store.Output: When called, addUser("Alice", 25)
and addUser("Bob", 30)
add two users to the “users” object store.
Reading data is similar to writing; it also uses transactions, but typically in read-only mode.
function getUser(id) {
const transaction = db.transaction(["users"], "readonly");
const objectStore = transaction.objectStore("users");
const request = objectStore.get(id);
request.onsuccess = function(event) {
if (request.result) {
console.log("User found:", request.result);
} else {
console.log("User not found.");
}
};
request.onerror = function() {
console.error("Error retrieving user.");
};
}
getUser(1); // Assuming "Alice" has id 1
transaction
: Creates a read-only transaction.objectStore.get
: Retrieves an object by key (id
).Output: If a user with the given ID exists, it’s logged; otherwise, a message says “User not found.”
Updating data requires opening a transaction with read-write permissions.
function updateUser(id, updatedData) {
const transaction = db.transaction(["users"], "readwrite");
const objectStore = transaction.objectStore("users");
const request = objectStore.get(id);
request.onsuccess = function(event) {
const data = event.target.result;
if (data) {
for (let key in updatedData) {
data[key] = updatedData[key];
}
const updateRequest = objectStore.put(data);
updateRequest.onsuccess = () => console.log("User updated:", data);
} else {
console.log("User not found.");
}
};
request.onerror = () => console.error("Error updating user.");
}
updateUser(1, { age: 26 });
objectStore.get
: Retrieves the object to be updated.objectStore.put
: Saves the updated object back into the store.Output: Updates the age of the user with ID 1
to 26
.
Deleting data uses the delete
method in a transaction.
function deleteUser(id) {
const transaction = db.transaction(["users"], "readwrite");
const objectStore = transaction.objectStore("users");
const request = objectStore.delete(id);
request.onsuccess = function() {
console.log("User deleted.");
};
request.onerror = function() {
console.error("Error deleting user.");
};
}
deleteUser(2); // Deletes user with ID 2
objectStore.delete
: Deletes an entry by key.onsuccess
: Confirms successful deletion.Output: Deletes the specified user and logs “User deleted.”
IndexedDB operates on transactions, which are essential for managing multiple database operations and ensuring data consistency. Transactions can be read-only or readwrite and must complete before new operations can start.
const transaction = db.transaction(["users"], "readonly");
const objectStore = transaction.objectStore("users");
transaction.oncomplete = function() {
console.log("Transaction completed successfully.");
};
transaction.onerror = function() {
console.error("Transaction error.");
};
IndexedDB provides a robust solution for managing large, structured data within the browser, offering far more flexibility and scalability than localStorage. From adding data to reading, updating, and deleting records, IndexedDB operations rely heavily on asynchronous transactions. By understanding and effectively using IndexedDB, developers can create responsive, data-heavy applications that work seamlessly offline and manage data on the client side with precision and efficiency. Happy coding !❤️