Dexie.js is a minimalistic wrapper for IndexedDB - the standard database in the browser. It provides a reactive, promise-based API with advanced querying capabilities, automatic transaction management, and schema evolution. Widely used by over 100,000 websites and applications.
npm install dexieimport Dexie from "dexie";Named imports:
import { Dexie, liveQuery, cmp } from "dexie";For CommonJS:
const Dexie = require("dexie");Legacy script tag:
<script src="https://unpkg.com/dexie/dist/dexie.js"></script>import Dexie from "dexie";
// Define database
const db = new Dexie("MyDatabase");
db.version(1).stores({
friends: "++id, name, age",
messages: "++id, friendId, message, timestamp"
});
// CRUD operations
await db.friends.add({ name: "Alice", age: 25 });
const friend = await db.friends.get(1);
const adults = await db.friends.where("age").above(18).toArray();
await db.friends.update(1, { age: 26 });
await db.friends.delete(1);
// Live queries (reactive)
import { liveQuery } from "dexie";
const friends$ = liveQuery(() => db.friends.toArray());
friends$.subscribe(friends => console.log("Friends updated:", friends));Dexie.js is built around several key components:
Dexie class handles connections, schema versioning, and transactionsTable class provides CRUD operations and query initiationWhereClause and Collection classes offer chainable query methodsCore database functionality including connection management, schema definition, versioning, and transaction handling.
class Dexie {
constructor(name: string, options?: DexieOptions);
version(versionNumber: number): Version;
open(): PromiseExtended<Dexie>;
close(options?: { disableAutoOpen?: boolean }): void;
delete(options?: { disableAutoOpen?: boolean }): Promise<void>;
transaction<T>(mode: TransactionMode, tables: string[], scope: () => T | PromiseLike<T>): Promise<T>;
table(tableName: string): Table<any, any>;
}
interface DexieOptions {
addons?: Array<(db: Dexie) => void>;
autoOpen?: boolean;
indexedDB?: IDBFactory;
IDBKeyRange?: typeof IDBKeyRange;
allowEmptyDB?: boolean;
modifyChunkSize?: number;
chromeTransactionDurability?: "default" | "strict" | "relaxed";
cache?: "immutable" | "cloned" | "disabled";
}
type TransactionMode = "r" | "rw" | "readonly" | "readwrite";CRUD operations, bulk operations, and query initiation for database tables.
interface Table<T = any, TKey = IndexableType, TInsertType = T> {
// CRUD operations
get(key: IndexableType): Promise<T | undefined>;
add(item: TInsertType, key?: TKey): Promise<TKey>;
put(item: TInsertType, key?: TKey): Promise<TKey>;
update(key: IndexableType, changes: UpdateSpec<T>): Promise<number>;
delete(key: IndexableType): Promise<void>;
clear(): Promise<void>;
// Query initiation
where(index: string): WhereClause<T, TKey, TInsertType>;
filter(predicate: (obj: T) => boolean): Collection<T, TKey, TInsertType>;
orderBy(index: string): Collection<T, TKey, TInsertType>;
// Bulk operations
bulkAdd(items: readonly TInsertType[], keys?: readonly TKey[], options?: BulkOptions): Promise<TKey>;
bulkPut(items: readonly TInsertType[], keys?: readonly TKey[], options?: BulkOptions): Promise<TKey>;
bulkUpdate(changes: readonly { key: IndexableType; changes: UpdateSpec<T> }[]): Promise<number>;
bulkDelete(keys: readonly IndexableType[]): Promise<void>;
bulkGet(keys: readonly IndexableType[]): Promise<(T | undefined)[]>;
}
type IndexableType = string | number | Date | ArrayBuffer | ArrayBufferView | DataView | Array<Array<void>>;
type UpdateSpec<T> = Partial<T> | { [keyPath: string]: any };
interface BulkOptions {
allKeys?: boolean;
ignoreOnsuccess?: boolean;
}Advanced query building with indexed lookups, filtering, and result manipulation.
interface WhereClause<T = any, TKey = IndexableType, TInsertType = T> {
// Comparison operators
equals(value: IndexableType): Collection<T, TKey, TInsertType>;
above(value: any): Collection<T, TKey, TInsertType>;
aboveOrEqual(value: any): Collection<T, TKey, TInsertType>;
below(value: any): Collection<T, TKey, TInsertType>;
belowOrEqual(value: any): Collection<T, TKey, TInsertType>;
between(lower: any, upper: any, includeLower?: boolean, includeUpper?: boolean): Collection<T, TKey, TInsertType>;
// String operations
startsWith(prefix: string): Collection<T, TKey, TInsertType>;
startsWithIgnoreCase(prefix: string): Collection<T, TKey, TInsertType>;
// Array operations
anyOf(keys: readonly IndexableType[]): Collection<T, TKey, TInsertType>;
noneOf(keys: readonly IndexableType[]): Collection<T, TKey, TInsertType>;
inAnyRange(ranges: readonly { 0: any; 1: any }[], options?: RangeOptions): Collection<T, TKey, TInsertType>;
}
interface Collection<T = any, TKey = IndexableType, TInsertType = T> {
// Query refinement
and(predicate: (obj: T) => boolean): Collection<T, TKey, TInsertType>;
filter(predicate: (obj: T) => boolean): Collection<T, TKey, TInsertType>;
limit(count: number): Collection<T, TKey, TInsertType>;
offset(count: number): Collection<T, TKey, TInsertType>;
reverse(): Collection<T, TKey, TInsertType>;
// Execution methods
toArray(): Promise<T[]>;
count(): Promise<number>;
first(): Promise<T | undefined>;
each(callback: (obj: T, cursor: { key: any; primaryKey: TKey }) => void): Promise<void>;
// Mutation methods
modify(changes: UpdateSpec<T> | ((obj: T, ctx: { value: T }) => void)): Promise<number>;
delete(): Promise<number>;
}
interface RangeOptions {
includeLowers?: boolean;
includeUppers?: boolean;
}Database versioning, schema definition, and migration management.
interface Version {
stores(schema: { [tableName: string]: string | null }): Version;
upgrade(upgradeFunction: (trans: Transaction) => PromiseLike<any> | void): Version;
}
interface Transaction {
db: Dexie;
active: boolean;
mode: IDBTransactionMode;
storeNames: string[];
table(tableName: string): Table<any, any>;
abort(): void;
}Reactive query system that automatically updates when underlying data changes.
function liveQuery<T>(querier: () => T | Promise<T>): Observable<T>;
interface Observable<T> {
subscribe(observer: Observer<T> | ((value: T) => void)): Subscription;
}
interface Observer<T> {
next: (value: T) => void;
error?: (error: any) => void;
complete?: () => void;
}
interface Subscription {
unsubscribe(): void;
}Helper functions for comparison, property modification, and range operations.
function cmp(a: any, b: any): number;
class PropModification {
constructor(spec: PropModSpec);
execute(value: any): any;
}
function add(value: number | bigint | any[]): PropModification;
function remove(value: number | bigint | any[]): PropModification;
function replacePrefix(oldPrefix: string, newPrefix: string): PropModification;
class RangeSet {
constructor(fromOrTree?: any, to?: any);
add(rangeOrKey: any): RangeSet;
addKey(key: any): RangeSet;
addKeys(keys: any[]): RangeSet;
}
function mergeRanges(target: IntervalTree, newSet: IntervalTree): void;
function rangesOverlap(rangeSet1: IntervalTree, rangeSet2: IntervalTree): boolean;Comprehensive error handling system with specific error types, enhanced promise catching, and recovery strategies.
class DexieError extends Error {
name: string;
stack?: string;
inner?: any;
}
// Dexie-specific errors
class OpenFailedError extends DexieError {}
class SchemaError extends DexieError {}
class TransactionInactiveError extends DexieError {}
class ReadOnlyError extends DexieError {}
// Enhanced promise catching
interface PromiseExtended<T> extends Promise<T> {
catch<TResult = never>(
onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | undefined | null
): PromiseExtended<T | TResult>;
catch<TResult = never>(
ExceptionType: Function,
onrejected?: (reason: any) => TResult | PromiseLike<TResult>
): PromiseExtended<T | TResult>;
}Database and table-level event system for validation, auditing, and extending functionality.
interface DexieEvent {
fire(...args: any[]): any;
subscribe(fn: (...args: any[]) => any): void;
unsubscribe(fn: (...args: any[]) => any): void;
}
interface DbEvents {
ready: DexieEvent;
populate: DexieEvent;
blocked: DexieEvent;
versionchange: DexieEvent;
close: DexieEvent;
}
interface TableHooks<T = any, TKey = any> {
creating: DexieEvent;
reading: DexieEvent;
updating: DexieEvent;
deleting: DexieEvent;
}type PromiseExtended<T> = Promise<T> & {
timeout(ms: number, errorMessage?: string | Error): PromiseExtended<T>;
};
interface TableSchema {
name: string;
primKey: IndexSpec;
indexes: IndexSpec[];
mappedClass?: Function;
}
interface IndexSpec {
name: string;
keyPath: string | string[];
unique?: boolean;
multi?: boolean;
auto?: boolean;
compound?: boolean;
src: string;
}
abstract class Entity {
protected _db?: Dexie;
protected table(): Table<this>;
}
interface DexieEvent {
subscribers: Function[];
fire(...args: any[]): any;
subscribe(fn: (...args: any[]) => any): void;
unsubscribe(fn: (...args: any[]) => any): void;
}