Skip to content

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)

ParameterDescriptionTypeDefault
dbNameDatabase namestringRequired
versionSchema version; bump it whenever stores changesnumber1
storesDeclarative object stores + indexes, created during an upgradeIDBStoreSchema[][]
upgradeEscape hatch for migrations the schema cannot express, run after storesFunction

Every method resolves or rejects with the same IDBResult shape, so callers only check error.

MethodDescription
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.

MemberReturnsOn 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
js
const notes = db.collection('books_notes');
await notes.put(note); // false if it failed
const all = await notes.all(); // [] if it failed

Pick 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

js
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

  1. 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 stores array across versions.
  2. Version downgrades self-heal. Opening with a version lower than what is on disk throws a VersionError; WebDB parses the real version out of it, realigns and reopens instead of surfacing the error.
  3. 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.
  4. Pair it with singleFlight so concurrent callers share one open: const ready = singleFlight(() => db.openDataBase()).

Released under the MIT License.