A small wrapper that makes IndexedDB usable with promises and enhanced TypeScript support
npx @tessl/cli install tessl/npm-idb@8.0.0IDB is a lightweight TypeScript wrapper around the browser's IndexedDB API that significantly improves usability while maintaining the core functionality. It transforms callback-based IDBRequest objects into promises, adds convenient shortcut methods for common database operations, provides enhanced transaction handling with automatic lifetime management, and includes TypeScript support with strongly-typed database schemas.
npm install idbimport { openDB, deleteDB, wrap, unwrap } from "idb";For CommonJS:
const { openDB, deleteDB, wrap, unwrap } = require("idb");import { openDB } from "idb";
// Define database schema
interface MyDB {
people: {
key: number;
value: {
name: string;
age: number;
};
indexes: { name: string };
};
}
// Open database with schema
const db = await openDB<MyDB>("my-database", 1, {
upgrade(db) {
const peopleStore = db.createObjectStore("people", {
keyPath: "id",
autoIncrement: true,
});
peopleStore.createIndex("name", "name");
},
});
// Use database with shortcut methods
await db.add("people", { name: "Alice", age: 30 });
const person = await db.get("people", 1);
const allPeople = await db.getAll("people");
// Iterate over all records using async iteration
const tx = db.transaction("people");
for await (const cursor of tx.store) {
console.log(cursor.key, cursor.value);
}IDB is built around several key components:
openDB and deleteDB functions for database lifecycle managementwrap/unwrap system converting IDB objects to promise-based equivalentsIDBPDatabase, IDBPTransaction, etc.) with promise-based methodsfor await loops over cursors, stores, and indexesCore database lifecycle management including opening databases with upgrade callbacks, deleting databases, and handling database events.
function openDB<DBTypes extends DBSchema | unknown = unknown>(
name: string,
version?: number,
{ blocked, upgrade, blocking, terminated }: OpenDBCallbacks<DBTypes> = {}
): Promise<IDBPDatabase<DBTypes>>;
function deleteDB(
name: string,
{ blocked }: DeleteDBCallbacks = {}
): Promise<void>;Low-level utilities for converting native IndexedDB objects to promise-based equivalents and vice versa.
function wrap(value: IDBDatabase): IDBPDatabase;
function wrap(value: IDBRequest<T>): Promise<T>;
function unwrap(value: IDBPDatabase): IDBDatabase;
function unwrap<T>(value: Promise<T>): IDBRequest<T>;
function replaceTraps(
callback: (currentTraps: ProxyHandler<any>) => ProxyHandler<any>
): void;Promise-based database interface with shortcut methods for common operations, eliminating the need for explicit transaction management in simple cases.
interface IDBPDatabase<DBTypes extends DBSchema | unknown = unknown> {
// Transaction methods
transaction<Name extends StoreNames<DBTypes>, Mode extends IDBTransactionMode = 'readonly'>(
storeNames: Name,
mode?: Mode,
options?: IDBTransactionOptions
): IDBPTransaction<DBTypes, [Name], Mode>;
// Shortcut methods
add<Name extends StoreNames<DBTypes>>(
storeName: Name,
value: StoreValue<DBTypes, Name>,
key?: StoreKey<DBTypes, Name> | IDBKeyRange
): Promise<StoreKey<DBTypes, Name>>;
get<Name extends StoreNames<DBTypes>>(
storeName: Name,
query: StoreKey<DBTypes, Name> | IDBKeyRange
): Promise<StoreValue<DBTypes, Name> | undefined>;
put<Name extends StoreNames<DBTypes>>(
storeName: Name,
value: StoreValue<DBTypes, Name>,
key?: StoreKey<DBTypes, Name> | IDBKeyRange
): Promise<StoreKey<DBTypes, Name>>;
}Enhanced transaction interface with automatic completion handling and convenient store access.
interface IDBPTransaction<
DBTypes extends DBSchema | unknown = unknown,
TxStores extends ArrayLike<StoreNames<DBTypes>> = ArrayLike<StoreNames<DBTypes>>,
Mode extends IDBTransactionMode = 'readonly'
> {
readonly mode: Mode;
readonly objectStoreNames: TypedDOMStringList<TxStores[number]>;
readonly db: IDBPDatabase<DBTypes>;
readonly done: Promise<void>;
readonly store: TxStores[1] extends undefined
? IDBPObjectStore<DBTypes, TxStores, TxStores[0], Mode>
: undefined;
objectStore<StoreName extends TxStores[number]>(
name: StoreName
): IDBPObjectStore<DBTypes, TxStores, StoreName, Mode>;
}Enhanced object store interface with promise-based operations and async iteration support.
interface IDBPObjectStore<
DBTypes extends DBSchema | unknown = unknown,
TxStores extends ArrayLike<StoreNames<DBTypes>> = ArrayLike<StoreNames<DBTypes>>,
StoreName extends StoreNames<DBTypes> = StoreNames<DBTypes>,
Mode extends IDBTransactionMode = 'readonly'
> {
// Core operations
add: Mode extends 'readonly' ? undefined : (
value: StoreValue<DBTypes, StoreName>,
key?: StoreKey<DBTypes, StoreName> | IDBKeyRange
) => Promise<StoreKey<DBTypes, StoreName>>;
get(
query: StoreKey<DBTypes, StoreName> | IDBKeyRange
): Promise<StoreValue<DBTypes, StoreName> | undefined>;
// Iteration support
[Symbol.asyncIterator](): AsyncIterableIterator<
IDBPCursorWithValueIteratorValue<DBTypes, TxStores, StoreName, unknown, Mode>
>;
}Enhanced index interface for querying data by secondary keys with full async iteration support.
interface IDBPIndex<
DBTypes extends DBSchema | unknown = unknown,
TxStores extends ArrayLike<StoreNames<DBTypes>> = ArrayLike<StoreNames<DBTypes>>,
StoreName extends StoreNames<DBTypes> = StoreNames<DBTypes>,
IndexName extends IndexNames<DBTypes, StoreName> = IndexNames<DBTypes, StoreName>,
Mode extends IDBTransactionMode = 'readonly'
> {
readonly objectStore: IDBPObjectStore<DBTypes, TxStores, StoreName, Mode>;
get(
query: IndexKey<DBTypes, StoreName, IndexName> | IDBKeyRange
): Promise<StoreValue<DBTypes, StoreName> | undefined>;
openCursor(
query?: IndexKey<DBTypes, StoreName, IndexName> | IDBKeyRange | null,
direction?: IDBCursorDirection
): Promise<IDBPCursorWithValue<DBTypes, TxStores, StoreName, IndexName, Mode> | null>;
}Enhanced cursor interfaces with promise-based navigation and async iteration support for efficient data traversal.
interface IDBPCursor<
DBTypes extends DBSchema | unknown = unknown,
TxStores extends ArrayLike<StoreNames<DBTypes>> = ArrayLike<StoreNames<DBTypes>>,
StoreName extends StoreNames<DBTypes> = StoreNames<DBTypes>,
IndexName extends IndexNames<DBTypes, StoreName> | unknown = unknown,
Mode extends IDBTransactionMode = 'readonly'
> {
readonly key: IndexName extends IndexNames<DBTypes, StoreName>
? IndexKey<DBTypes, StoreName, IndexName>
: StoreKey<DBTypes, StoreName>;
readonly primaryKey: StoreKey<DBTypes, StoreName>;
advance<T>(this: T, count: number): Promise<T | null>;
continue<T>(this: T, key?: IndexName extends IndexNames<DBTypes, StoreName>
? IndexKey<DBTypes, StoreName, IndexName>
: StoreKey<DBTypes, StoreName>): Promise<T | null>;
[Symbol.asyncIterator](): AsyncIterableIterator<
IDBPCursorIteratorValue<DBTypes, TxStores, StoreName, IndexName, Mode>
>;
}
interface IDBPCursorWithValue<...> extends IDBPCursor<...> {
readonly value: StoreValue<DBTypes, StoreName>;
}interface DBSchema {
[s: string]: DBSchemaValue;
}
interface DBSchemaValue {
key: IDBValidKey;
value: any;
indexes?: IndexKeys;
}
interface IndexKeys {
[s: string]: IDBValidKey;
}
interface OpenDBCallbacks<DBTypes extends DBSchema | unknown> {
upgrade?(
database: IDBPDatabase<DBTypes>,
oldVersion: number,
newVersion: number | null,
transaction: IDBPTransaction<DBTypes, StoreNames<DBTypes>[], 'versionchange'>,
event: IDBVersionChangeEvent
): void;
blocked?(currentVersion: number, blockedVersion: number | null, event: IDBVersionChangeEvent): void;
blocking?(currentVersion: number, blockedVersion: number | null, event: IDBVersionChangeEvent): void;
terminated?(): void;
}
interface DeleteDBCallbacks {
blocked?(currentVersion: number, event: IDBVersionChangeEvent): void;
}
type StoreNames<DBTypes extends DBSchema | unknown> =
DBTypes extends DBSchema ? KnownKeys<DBTypes> : string;
type StoreValue<DBTypes extends DBSchema | unknown, StoreName extends StoreNames<DBTypes>> =
DBTypes extends DBSchema ? DBTypes[StoreName]['value'] : any;
type StoreKey<DBTypes extends DBSchema | unknown, StoreName extends StoreNames<DBTypes>> =
DBTypes extends DBSchema ? DBTypes[StoreName]['key'] : IDBValidKey;
type IndexNames<DBTypes extends DBSchema | unknown, StoreName extends StoreNames<DBTypes>> =
DBTypes extends DBSchema ? keyof DBTypes[StoreName]['indexes'] & string : string;
type IndexKey<
DBTypes extends DBSchema | unknown,
StoreName extends StoreNames<DBTypes>,
IndexName extends IndexNames<DBTypes, StoreName>
> = DBTypes extends DBSchema
? IndexName extends keyof DBTypes[StoreName]['indexes']
? DBTypes[StoreName]['indexes'][IndexName]
: IDBValidKey
: IDBValidKey;
interface TypedDOMStringList<T extends string> extends DOMStringList {
contains(string: T): boolean;
item(index: number): T | null;
[index: number]: T;
[Symbol.iterator](): IterableIterator<T>;
}
interface IDBTransactionOptions {
durability?: 'default' | 'strict' | 'relaxed';
}