WebDB
A Promise wrapper around IndexedDB. Native IndexedDB is event-callback + transaction based — every read or write is five steps (open → transaction → objectStore → request → onsuccess/onerror). WebDB collapses that to await db.add({ storeName, data }).
API
new WebDB(options)
| Parameter | Description | Type | Default |
|---|---|---|---|
dbName | Database name | string | Required |
version | Schema version; bump it whenever stores changes | number | 1 |
stores | Declarative object stores + indexes, created during an upgrade | IDBStoreSchema[] | [] |
upgrade | Escape hatch for migrations the schema cannot express, run after stores | Function | — |
Every method resolves or rejects with the same IDBResult shape, so callers only check error.
| Method | Description |
|---|---|
openDataBase() | Open (and upgrade if needed) |
closeDataBase() | Close and drop the handle |
refreshDatabase() | Close then reopen |
deleteDatabase() | Delete the database |
add({ storeName, data }) | Insert; fails if the key exists |
update({ storeName, data }) | Put — insert or overwrite |
readByKey({ storeName, key }) | Read one record |
readAll({ storeName, query?, count? }) | Read every record |
readByCursor({ storeName, keyRange?, direction? }) | Walk with a cursor |
count({ storeName, query? }) | Count records |
delete({ storeName, key }) | Delete one record |
clear({ storeName }) | Empty a store |
db.collection<T>(name)
A typed, forgiving handle on one store. Binds the store name once and returns plain values instead of IDBResult.
| Member | Returns | On failure |
|---|---|---|
get(key) | Promise<T | null> | null |
all() | Promise<T[]> | [] |
count() | Promise<number> | 0 |
add(value) | Promise<boolean> | false |
put(value) | Promise<boolean> | false |
remove(key) | Promise<boolean> | false |
clear() | Promise<boolean> | false |
const notes = db.collection('books_notes');
await notes.put(note); // false if it failed
const all = await notes.all(); // [] if it failedPick the right layer
Every method swallows the error. That is the right default for what most apps keep in IndexedDB — reading progress, drafts, caches — where a failed read should degrade the feature, not take down the screen. It is the wrong default when the write is the user's action (saving a document, completing a purchase): there, call the IDBResult methods above and handle the failure.
Example
import { WebDB } from 'ranuts';
const db = new WebDB({
dbName: 'read',
version: 4,
stores: [
{ name: 'books', options: { keyPath: 'id' }, indexes: [{ name: 'byAuthor', keyPath: 'author' }] },
{ name: 'notes', options: { keyPath: 'id' } },
],
});
await db.openDataBase();
await db.add({ storeName: 'books', data: { id: '1', title: 'Walden' } });
const { data } = await db.readByKey({ storeName: 'books', key: '1' });Notes
- Stores can only be created inside an upgrade transaction — that's why they are declared up front rather than created after the database opens. Creating a missing store or index is idempotent, so it is safe to keep the same
storesarray across versions. - Version downgrades self-heal. Opening with a version lower than what is on disk throws a
VersionError;WebDBparses the real version out of it, realigns and reopens instead of surfacing the error. - Upgrades are never blocked by this connection. Each open registers
onversionchange, so when another tab or worker asks for a higher version this connection closes itself. - Pair it with
singleFlightso concurrent callers share one open:const ready = singleFlight(() => db.openDataBase()).