Frontend Labs
⋯

45. IndexedDB: transactional storage for structured, larger data

IndexedDB is a full asynchronous, transactional, indexed database built into the browser — the right tool once data outgrows Web Storage's 5-10MB string-only model (offline app data, cached API responses, encrypted blobs from §46-48).

function openDB() {
  return new Promise((resolve, reject) => {
    const req = indexedDB.open("notes-app", 1);
    req.onupgradeneeded = (e) => {
      const db = e.target.result;
      const store = db.createObjectStore("notes", { keyPath: "id" });
      store.createIndex("by-date", "createdAt");   // queryable secondary index
    };
    req.onsuccess = () => resolve(req.result);
    req.onerror = () => reject(req.error);
  });
}

async function addNote(db, note) {
  return new Promise((resolve, reject) => {
    const tx = db.transaction("notes", "readwrite");
    tx.objectStore("notes").add(note);
    tx.oncomplete = () => resolve();
    tx.onerror = () => reject(tx.error);
  });
}

async function getAllNotesSortedByDate(db) {
  return new Promise((resolve, reject) => {
    const tx = db.transaction("notes", "readonly");
    const index = tx.objectStore("notes").index("by-date");
    const results = [];
    index.openCursor().onsuccess = (e) => {
      const cursor = e.target.result;
      if (cursor) { results.push(cursor.value); cursor.continue(); }
      else resolve(results);
    };
    tx.onerror = () => reject(tx.error);
  });
}

The raw callback-based API is notoriously verbose — in real projects, wrap it with a promise-based library (idb is the standard choice) rather than hand-rolling this every time; the example above shows what that library is doing underneath so debugging a leaked transaction or a version-upgrade bug isn't a black box.

Generated from real-life-JS-guide.md · 8 Oct 2026 16:36