- read

Learn IndexedDB - Creating a CRUD application with IndexedDB and React

Suman Kumar 45

Learn IndexedDB - Creating a CRUD application with IndexedDB and React

Suman Kumar
Level Up Coding
Published in
6 min read5 hours ago

--

Photo by Jan Antonin Kolar on Unsplash

Have you ever heard about IndexedDB? If not then what an IndexedDB is?

IndexedDB, short for Indexed Database, is a low-level, web-based database system that is part of the web platform’s set of APIs (Application Programming Interfaces). It provides a way for web applications to store and manage structured data on the client side (in the user’s web browser) for offline access, caching, and improved performance.

Some key characteristics and features of IndexedDB:

  1. Structured Data: Unlike traditional web storage mechanisms like cookies or Web Storage (localStorage and sessionStorage), IndexedDB allows you to store structured data, including JavaScript objects, in a key-value store.
  2. Indexed Data: One of the significant advantages of IndexedDB is its ability to create indexes on the data. These indexes allow for efficient querying and retrieval of data, making them suitable for applications that require complex data retrieval.
  3. Transactions: IndexedDB operates within the context of transactions. This ensures data consistency and helps prevent data corruption when multiple operations are being performed simultaneously.
  4. Asynchronous: Most IndexedDB operations are asynchronous, meaning they do not block the main thread of the web page. This is crucial for maintaining a responsive user interface.
  5. Scalable: IndexedDB can handle a significant amount of data, making it suitable for applications with large data sets.
  6. Cross-Origin: IndexedDB follows the same-origin policy, which means it typically restricts access to databases to pages from the same origin (i.e., the same protocol, domain, and port). However, it is possible to use techniques like Cross-Origin Resource Sharing (CORS) to allow cross-origin access in some cases.
  7. Security: IndexedDB is more secure than traditional web storage options because it doesn’t send data to the server unless explicitly programmed to do so. This makes it a better choice for storing sensitive information on the client side.

Implementation

Now, we know what an indexedDB is. Let’s create a CRUD application with IndexedDB and ReactJs and understand the implementation step by step.

First, we will check if the given browser has an indexedDB or not. We will define a const “idb” and assign the indexed object to it.

const idb =
window.indexedDB ||
window.mozIndexedDB ||
window.webkitIndexedDB ||
window.msIndexedDB ||
window.shimIndexedDB;

In the above code, we are checking whether the window object has the indexedDB or not, and here the “window.indexedDB” refers to Chrome, “window.mozIndexedDB” refers to Firefox, etc.

Now, when our application loads the first time we will push two records in our database “test-db” in the “userData” collection.

Let’s create a function “insertDataInIndexedDb” for this, in which we will first check if the browser has an indexedDB or not.

 if (!idb) {
console.log("This browser doesn't support IndexedDB");
return;
}

Now, we will create a connection to the database “test-db” and the same script will create a DB if it does not exist.

const request = idb.open("test-db", 1);

First, we will listen to the “onerror” method and check if there’s any error in connecting to the database.

 request.onerror = function (event) {
console.error("An error occurred with IndexedDB");
console.error(event);
};

Then, we will listen to the “onupgradeneeded” method, if it is successfully connected then we will create “userData” collection and define “keyPath” as “id”. FYI “keyPath” is the same as “_id” in the MongoDB.

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

if (!db.objectStoreNames.contains("userData")) {
const objectStore = db.createObjectStore("userData", { keyPath: "id" });
}
};

Below is the sample data which we want to add to the collection “userData”.

export const USER_DATA = [
{
id: 1,
firstName: "Suman",
lastName: "Kumar",
email: "[email protected]",
age: 10
},
{
id: 2,
firstName: "Rahul",
lastName: "Kumar",
email: "[email protected]",
age: 15
},
];

Now, we will listen to the “onsuccess” method on “request”. It will run after the other two methods and we will push the above data into the userData collection in this method. For inserting we will user “add” query on collection “userData”.

request.onsuccess = function () {
console.log("Database opened successfully");

const db = request.result;

var tx = db.transaction("userData", "readwrite");
var userData = tx.objectStore("userData");

USER_DATA.forEach((item) => userData.add(item));

return tx.complete;
};

Now, if you check the developer tools of your browser, you will find the ‘test-db’ database and ‘userData’ collection.

Developer tools showing indexedDB

Now, that we have inserted the data, let’s try to fetch this data from indexedDB.

We will create a function “getAllData” in which we will establish a connection with DB and in the ‘onsuccess’ method we will run the transaction to get the userData as read-only.

And we will run the “userData.getAll()” query to get the users. Then, on the “onsuccess” method of this query, we will get the results and set it in the local state.

const [allUsers, setAllUsers] = useState([]);

const getAllData = () => {
const dbPromise = idb.open("test-db", 1);
dbPromise.onsuccess = () => {
const db = dbPromise.result;

var tx = db.transaction("userData", "readonly");
var userData = tx.objectStore("userData");
const users = userData.getAll();

users.onsuccess = (query) => {
setAllUsers(query.srcElement.result);
};

tx.oncomplete = function () {
db.close();
};
};
};

So, now after adding these users to the table, it will look like this.

Table showing data fetched from IndexedDB

Now, we will create a form to add a user to the “userData” collection.

Form to add a user

To submit the data of this form to the DB, we will create a function “handleSubmit”. In this function, we will first establish a connection with the database. And if the connection is successful then we will run the “put” method to insert data in DB. “Put” will be an upsert method based on “id” and after the successful execution of the query, we will close the DB connection.

 const handleSubmit = (event) => {
const dbPromise = idb.open("test-db", 1);

if (firstName && lastName && email) {
dbPromise.onsuccess = () => {
const db = dbPromise.result;

var tx = db.transaction("userData", "readwrite");
var userData = tx.objectStore("userData");

const users = userData.put({
id: allUsers?.length + 1,
firstName,
lastName,
email,
age,
});

console.log("add");
users.onsuccess = (query) => {
tx.oncomplete = function () {
db.close();
};
alert("User added!");
event.preventDefault();
};
} else {
alert("Please enter all details");
}
};

The same, function will work for the “Update” query. Here we will set a variable in the state “addUser” to differentiate between add and edit functions.

 const handleSubmit = (event) => {
const dbPromise = idb.open("test-db", 1);
console.log(addUser, editUser);

if (firstName && lastName && email) {
dbPromise.onsuccess = () => {
const db = dbPromise.result;

var tx = db.transaction("userData", "readwrite");
var userData = tx.objectStore("userData");

console.log(addUser, editUser);
console.log(addUser, editUser);
if (addUser) {
const users = userData.put({
id: allUsers?.length + 1,
firstName,
lastName,
email,
age,
});

console.log("add");
users.onsuccess = (query) => {
tx.oncomplete = function () {
db.close();
};
alert("User added!");
event.preventDefault();
};
} else {
const users = userData.put({
id: selectedUser?.id,
firstName,
lastName,
email,
age,
});
console.log("edit");

users.onsuccess = (query) => {
tx.oncomplete = function () {
db.close();
};
alert("User updated!");
event.preventDefault();
};
}
};
} else {
alert("Please enter all details");
}
};

Now, the final is the delete function. In which we will run the “delete” query and after the successful query we will get the updated data and we will close the DB connection.

const deleteSelected = (user) => {
const dbPromise = idb.open("test-db", 1);

dbPromise.onsuccess = function () {
const db = dbPromise.result;
var tx = db.transaction("userData", "readwrite");
var userData = tx.objectStore("userData");
const deleteUser = userData.delete(user.id);

deleteUser.onsuccess = (query) => {
tx.oncomplete = function () {
db.close();
};
alert("User deleted!");
getAllData();
};
};
};

And if you check in the browser dev tools, you can see the data like this.

IndexedDB data

So, that’s all about the basic implementation of indexedDB.

GitHub Repo- https://github.com/sumankalia/Indexed_db_implementation/tree/main

Working Example- https://6512df0746f0680b2a3164a9--snazzy-marshmallow-03c5f5.netlify.app/

Link to watch the same implementation tutorial in Hindi on YouTube.

If you like the above article, please clap on it.

Thanks for reading.