IndexedDB Primer
A transactional object store baked into every modern browser — perfect for offline-first apps, large binary blobs, and structured data that outgrows localStorage.
Why IndexedDB
localStorage is synchronous, string-only, and capped at ~5 MB. IndexedDB is asynchronous, stores structured clones (objects, File, Blob, ArrayBuffer), and has no practical size limit — the browser prompts the user before crossing ~50 MB. It is the canonical storage layer for Progressive Web Apps and any tool that needs to survive a tab crash.
Mental model
IndexedDB is not a relational database. Think of it as a persistent Map where each key opens an object store. Every read or write happens inside a transaction. Transactions auto-commit when their microtask queue drains, so batch your puts.
The three-step dance
- indexedDB.open(name, version) — fires onupgradeneeded exactly once per version bump. Create stores and indexes here.
- Open a readwrite transaction on the store, get a reference, and call put, get, or getAll.
- Listen to transaction.oncomplete — the request-level success event fires before the transaction commits, so the durable signal is the transaction event.
Indexes and cursors
Create an index on a property to query without loading every record. Use IDBKeyRange.bound for range scans. Cursors let you walk results one-by-one, including deleting or updating in-place — essential for large datasets where getAll would allocate too much memory.
The promise wrapper you'll write anyway
The native API is event-driven. Wrap it in a thin promise layer that resolves on transaction complete and rejects on transaction error. Never mix raw callbacks and promises in the same transaction — the auto-commit behavior will surprise you.
Meridian tip: Use IndexedDB as the offline cache backing the license heartbeat. Store the last-known-good HMAC, machine fingerprint, and grace window expiry. On startup, read from IndexedDB before hitting the network — instant launch, even when the auth server is unreachable.