Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Practical IndexedDB Module for Structured Browser-Based Data Storage

Tech May 10 2

Overview

IndexedDB is a browser-based storage mechanism for structured data, resembling a relational database but offering higher capacity and greater flexibility. Unlike localStorage, IndexedDB is suitable for storing large volumes of data, handles complex structures, and operates asynchronously. It is a common choice for persistent client-side storage.

Key Features

  • Asynchronous: Most operasions are non-blocking to keep the UI thread responsive.
  • Flexible Data Structures: Stores JavaScript objects such as arrays, objects, and other structured data.
  • High Capacity: Limit are usually much larger than localStorage (typically 50 MB or more per origin in most browsers).
  • Transactional: Data operations run within transactions, ensuring atomic reads and writes.
  • Indexing: Support for indexes that accelerate data retrieval.

Typical Operations

  • Opening a database: Invoke indexedDB.open() to create or open a database.
  • Creating object stores: similar to tables, they hold the actual data.
  • Transactions: all writes and reads are performed inside transactions.
  • Data manipulation: get, put, delete and similar methods.
  • Index creation: define indexes on object properties to speed up queries.

Encapsulating IndexedDB in a Reusable Module

To make IndexedDB easier to use, the following module wraps common operations such as opening a database, saving data, retrieving data, and deleting records.

Module: storageEngine.js

/**
 * Open a (new or existing) database and ensure the given store is ready.
 */
export const initDatabase = (dbLabel, storeLabel) => {
  return new Promise((resolve, reject) => {
    const connectionReq = indexedDB.open(dbLabel, 1);

    connectionReq.onupgradeneeded = (event) => {
      const dbInstance = event.target.result;
      if (!dbInstance.objectStoreNames.contains(storeLabel)) {
        const objectStore = dbInstance.createObjectStore(storeLabel, { keyPath: 'recordId' });
        objectStore.createIndex('contentIndex', 'payload');
      }
    };

    connectionReq.onsuccess = (event) => resolve(event.target.result);
    connectionReq.onerror = (event) =>
      reject('Database open error: ' + event.target.errorCode);
  });
};

/**
 * Persist a document into the target store.
 */
export const persistDocument = (dbHandle, storeLabel, doc) => {
  return new Promise((resolve, reject) => {
    const txn = dbHandle.transaction(storeLabel, 'readwrite');
    const store = txn.objectStore(storeLabel);
    const putReq = store.put(doc);

    putReq.onsuccess = () => resolve('Document stored successfully');
    putReq.onerror = (event) =>
      reject('Store failed: ' + event.target.errorCode);
  });
};

/**
 * Fetch a single document by its primary key.
 */
export const fetchDocument = (dbHandle, storeLabel, recordId) => {
  return new Promise((resolve, reject) => {
    const txn = dbHandle.transaction(storeLabel, 'readonly');
    const store = txn.objectStore(storeLabel);
    const getReq = store.get(recordId);

    getReq.onsuccess = (event) => resolve(event.target.result);
    getReq.onerror = (event) =>
      reject('Fetch failed: ' + event.target.errorCode);
  });
};

/**
 * Remove a document identified by its primary key.
 */
export const deleteDocument = (dbHandle, storeLabel, recordId) => {
  return new Promise((resolve, reject) => {
    const txn = dbHandle.transaction(storeLabel, 'readwrite');
    const store = txn.objectStore(storeLabel);
    const delReq = store.delete(recordId);

    delReq.onsuccess = () => resolve('Document removed');
    delReq.onerror = (event) =>
      reject('Deletion failed: ' + event.target.errorCode);
  });
};

Usage Examples with React Hooks

Initializing the Database

const [dbConnection, setDbConnection] = useState(null);

useEffect(() => {
  const bootstrap = async () => {
    const connection = await initDatabase('AnalyticsDB', 'payloadStore');
    setDbConnection(connection);
  };
  bootstrap();
}, []);

Retrieving Data

const loadCachedPayload = async () => {
  if (!dbConnection) return fallbackPayload;
  try {
    const doc = await fetchDocument(dbConnection, 'payloadStore', currentKey);
    return doc ? doc.payload : fallbackPayload;
  } catch (err) {
    console.error('Unable to read from IndexedDB', err);
    return fallbackPayload;
  }
};

Saving Data

const commitPayload = async (newPayload) => {
  if (!dbConnection) return;
  try {
    await persistDocument(dbConnection, 'payloadStore', {
      recordId: currentKey,
      payload: newPayload,
    });
    console.log('Payload committed to IndexedDB');
  } catch (err) {
    console.error('Commit error', err);
  }
};

Deleting Data

const clearCachedPayload = async () => {
  if (!dbConnection) return;
  try {
    await deleteDocument(dbConnection, 'payloadStore', currentKey);
    console.log('Cached payload removed from IndexedDB');
  } catch (err) {
    console.error('Removal error', err);
  }
};

Summary

  • IndexedDB provides an asynchronous, transactional, and high-capacity storage service inside the browser.
  • The storageEngine.js module wraps common operations—database enitialization, data insertion, retrieval, and deletion.
  • By integrating these functions with React, you can manage structured client‑side data without running into the size limits of localStorage, while keeping complex data easily accessible.
Tags: IndexedDB

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.