indexed db is a client storage. It is similar to web storage but can save more data than web storage.
Think of indexed db as a database where you can create, add, edit, and delete. For every tasks, you have to connect to the database first.
Create
open(“dbName”, version): this opens a database or creates one if not exists. The schema of a database differs by the version.
const req = window.indexedDB.open("cars", 3);
open command not actually opens the database to work with. Instead, it returns if the request was successful or not. All the CRUD operation is done using ‘success’ event except for creation and version change which uses ‘.onupgradeneeded’
// seed data
const carData = [
{ id: "1", year: "2000", model: "BMW" },
{ id: "2", year: "2001", model: "Toyota" }
];
// access request
const req = indexedDB.open("cars", 3);
req.onerror = (event) => {
// Handle errors.
};
req.onupgradeneeded = (event) => { // if access was successful
const db = event.target.result;
// creates an object (like a table in a relational database)
const objectStore = db.createObjectStore("cars", { keyPath: "id" });
// creates indices(optional)
objectStore.createIndex("year", "year", { unique: false });
objectStore.createIndex("model", "model", { unique: false });
// checks if the object is created
objectStore.transaction.oncomplete = (event) => {
const carObjectStore = db.transaction("cars", "readwrite").objectStore("cars");
carData.forEach((car) => {
carObjectStore.add(car);
});
};
};
To check, open the developer tool -> select ‘Application’
Note that ‘year’ and ‘model’ under the ‘cars’ won be created if you omit the ‘createIndex’ code.
For the primary key, there is a auto increment option too.
const carData = [
{ year: "2000", model: "BMW" },
{ year: "2001", model: "Toyota" }
];
const req = indexedDB.open("cars", 3);
req.onerror = (event) => {
// Handle errors.
};
req.onupgradeneeded = (event) => {
const db = event.target.result;
// with auto increment
const objectStore = db.createObjectStore("cars", { keyPath: 'id', autoIncrement: true });
objectStore.createIndex("year", "year", { unique: false });
objectStore.createIndex("model", "model", { unique: false });
objectStore.transaction.oncomplete = (event) => {
const carObjectStore = db.transaction("cars", "readwrite").objectStore("cars");
carData.forEach((car) => {
carObjectStore.add(car);
});
};
};
For other tasks, we use transaction([“objectName”], “mode”). Second parameter of transaction has three options “readonly” (this is the default if omitted) for read, “readwrite” for add, update, and delete, and “versionchange” to change the version.
Read
1. Getting an item (A searched value must be unique)
const req = indexedDB.open("cars", 3);
req.onerror = (event) => {
// Handle errors.
};
req.onsuccess = (event) => {
const db = event.target.result;
const transaction = db.transaction(["cars"]);
const objectStore = transaction.objectStore("cars");
const data = objectStore.get("1");
data.onerror = (event) => {
// Handle errors
};
data.onsuccess = (event) => {
console.log(`BMW - ${data.result.year}`);
};
};
The same result but with a different syntax.
const req = indexedDB.open("cars", 3);
req.onerror = (event) => {
// Handle errors.
};
req.onsuccess = (event) => {
const db = event.target.result;
db.transaction("cars").objectStore("cars").get("1").onsuccess = (event) => {
console.log(`BMW - ${event.target.result.year}`);
};
};
2. Getting an item with an index (It returns the first match if there are many)
const req = indexedDB.open("cars", 3);
req.onerror = (event) => {
// Handle errors.
};
req.onsuccess = (event) => {
const db = event.target.result;
const transaction = db.transaction(["cars"]);
const objectStore = transaction.objectStore("cars");
const index = objectStore.index("model");
index.get("BMW").onsuccess = (event) => {
console.log(`BMW - ${event.target.result.year}`);
};
};
3. Getting all the items
To get all items we use .openCursor(). This returns the data in ascending order by default. If you want to change to descending order use .openCursor(“prev”).
const req = indexedDB.open("cars", 3);
req.onerror = (event) => {
// Handle errors.
};
req.onsuccess = (event) => {
const db = event.target.result;
const objectStore = db.transaction("cars").objectStore("cars");
objectStore.openCursor().onsuccess = (event) => {
const cursor = event.target.result;
if (cursor) {
console.log(`${cursor.key} is ${cursor.value.model}`);
cursor.continue();
} else {
console.log("Done");
}
};
};
Adding a range
// Only -> exact match
const singleKeyRange = IDBKeyRange.only("BMW");
// From the value specified
const lowerBoundKeyRange = IDBKeyRange.lowerBound("BMW");
// After the value specified
const lowerBoundOpenKeyRange = IDBKeyRange.lowerBound("BMW", true);
// Before the value specified
const upperBoundOpenKeyRange = IDBKeyRange.upperBound("Ford", true);
// bound("startRange", "endRange", startRange exclusion, endRange exclusion)
const boundKeyRange = IDBKeyRange.bound("1", "3", false, true);
const req = indexedDB.open("cars", 3);
req.onerror = (event) => {
// Handle errors.
};
req.onsuccess = (event) => {
const db = event.target.result;
const objectStore = db.transaction("cars").objectStore("cars");
objectStore.openCursor(boundKeyRange).onsuccess = (event) => {
const cursor = event.target.result;
if (cursor) {
if (cursor) {
// action console.log(`${cursor.key} is ${cursor.value.model}`);
cursor.continue();
} else {
console.log("Done");
}
}
};
};
Adding an item
const newCarData = [
{ id: "3", year: "2020", model: "Ford" },
];
const req = indexedDB.open("cars", 3);
req.onerror = (event) => {
// Handle errors.
};
req.onsuccess = (event) => {
const db = event.target.result;
const transaction = db.transaction(["cars"], "readwrite");
const objectStore = transaction.objectStore("cars");
transaction.oncomplete = (event) => {
// complete
};
transaction.onerror = (event) => {
// handle errors
};
newCarData.forEach((car) => {
const req = objectStore.add(car);
req.onerror = (event) => {
alert("failed");
};
req.onsuccess = (event) => {
alert("a new car added");
};
});
};
Make sure that you don’t omit the required value such as primary key. This won’t give you an error so hard to debug. You can omit optional values like shown below.
Update
const req = indexedDB.open("cars", 3);
req.onerror = (event) => {
// Handle errors.
};
req.onsuccess = (event) => {
const db = event.target.result;
const objectStore = db.transaction(["cars"], "readwrite").objectStore("cars");
const request = objectStore.get("1");
request.onerror = (event) => {
// Handle errors
};
request.onsuccess = (event) => {
// Get the old value that we want to update
const data = event.target.result;
// update
data.year = 1942;
// Put this updated object back into the database.
const requestUpdate = objectStore.put(data);
requestUpdate.onerror = (event) => {
alert("failed")
};
requestUpdate.onsuccess = (event) => {
alert("Updated")
};
};
};
Be mindful about the type as there is no type restriction
Delete
const req = indexedDB.open("cars", 3);
req.onerror = (event) => {
// Handle errors.
};
req.onsuccess = (event) => {
const db = event.target.result;
const request = db.transaction(["cars"], "readwrite")
.objectStore("cars")
.delete("3");
request.onsuccess = (event) => {
alert("deleted")
};
};
We have seen indexed db in this writing.
References
'Frontend > JavaScript' 카테고리의 다른 글
NPM - JSON Server (0) | 2023.05.22 |
---|---|
JavaScript Module - Text Wave Effect (2) | 2023.04.05 |
Client Side Storage - Web Storage (0) | 2023.03.31 |
Module System (0) | 2023.03.29 |
Webpack (1) | 2023.03.29 |