Make websites work offline - Offline Storage. Making IndexedDB the Hero!
Saurabh Daware 🌻
Posted on February 12, 2020
Note: This article does not expect you to know anything from Part 1.
Traditionally cookies were used for storing local data. But with HTML5 APIs, we got new options like localStorage
, sessionStorage
, WebSQL
, and IndexedDB
. In this article, we will specifically talk about IndexedDB.
Let's say you got Service Workers setup done and now your website loads offline. But... what if you want to store and retrieve a particular data? you cannot just fetch()
from your API since the user is offline.
In this case, you can store data in IndexedDB!
The Indexed Database API is a JavaScript application programming interface provided by web browsers for managing a NoSQL database of JSON objects. It is a standard maintained by the World Wide Web Consortium.
IndexedDB is provided by the browser and thus does not need internet for performing CRUD (Create Read Update Delete) operations. It is something like SQLite in Android (minus the SQL).
Implementation
If you prefer learning yourself from codesandbox, you can checkout IndexedDB Example.
For the browsers that use prefix, we can start with something like
window.indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;
window.IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.msIDBTransaction || {READ_WRITE: "readwrite"};
window.IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange || window.msIDBKeyRange;
if (!window.indexedDB) {
console.log("Your browser doesn't support a stable version of IndexedDB. Such and such feature will not be available.");
}
Before we go to the next code, let me warn you about something: IndexedDB is not promisified and thus is largely dependent on onsuccess
and onerror
callbacks. There are libraries like idb that provide promisified version of IndexedDB but for this article I will stick to the vanilla IndexedDB code.
Open/Create Database
Opening a database automatically creates new database if it doesn't exist
let db;
const request = indexedDB.open("MyTestDatabase");
request.onsuccess = function(event) {
db = event.target.result;
};
> Defining schema/values
When you create a new database, the onupgradeneeded
event will be triggered. We can create objectStores here,
request.onupgradeneeded = function() {
const db = event.target.result;
const userObjectStore = db.createObjectStore("users", {keyPath: "userid"});
userObjectStore.createIndex("name", "name", { unique: false });
userObjectStore.createIndex("email", "email", { unique: true });
}
Thus, the complete code to create/open a database would look something like:
async function openDatabase() {
return new Promise((resolve, reject) => {
const request = indexedDB.open("MyTestDatabase");
request.onsuccess = function(event) {
resolve(event.target.result);
}
request.onupgradeneeded = function() {
const db = event.target.result;
const userObjectStore = db.createObjectStore("users", {keyPath: "userid"});
userObjectStore.createIndex("name", "name", { unique: false });
userObjectStore.createIndex("email", "email", { unique: true });
}
})
}
openDatabase()
.then(db => {
// db instance accessible here
})
Add data
Now we have db
object accessible in openDatabase()
promise. We can use this object to add/read/delete the data from IndexedDB.
(async function() {
const db = await openDatabase();
// Add
const userReadWriteTransaction = db.transaction("users", "readwrite");
const newObjectStore = userReadWriteTransaction.objectStore("users");
newObjectStore.add({
userid: "4",
name: "John Doe",
email: "josn@gmail.com"
});
userReadWriteTransaction.onsuccess = function(e) {
console.log("Data Added");
}
})();
Remove data
const request = db.transaction("users", "readwrite")
.objectStore("users")
.delete("4");
request.onsuccess = function(event) {
console.log("Deleted!");
};
Read and Update Data
const readTransaction = db.transaction(["users"]);
const objectStore = transaction.objectStore("customers");
const request = objectStore.get("4");
request.onsuccess = function(event) {
console.log("User is " + request.result.name);
const data = event.target.result;
data.name = "John Doe";
const updateRequest = objectStore.put(data);
updateRequest.onsuccess = function(event) {
console.log("Data Updated!");
}
};
Example
Usecase?
If you have an API that always (or most of the times) returns same values, you can call API, store the response in IndexedDB and when next time user calls the API, you can return it from IndexedDB right there and maybe later call API and store the updated value.
I use IndexedDB in my application PocketBook which is a Google Keep alternative where you can store your todos, goals, etc. PocketBook uses IndexedDB by default to store notebook's information. Thus you can use pocketbook even when you are offline!
MDN Docs: https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API/Using_IndexedDB
codesandbox example: https://codesandbox.io/s/indexeddb-example-trv2f
PocketBook: https://pocketbook.cc
Thank you for reading! If you have any interesting project where you're using IndexedDB, do drop the link below!
Posted on February 12, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.