A pure JS in-memory implementation of the IndexedDB API for testing IndexedDB-dependent code in Node.js environments
npx @tessl/cli install tessl/npm-fake-indexeddb@6.2.0fake-indexeddb is a pure JavaScript in-memory implementation of the IndexedDB API specifically designed for testing IndexedDB-dependent code in Node.js environments. It provides complete compatibility with the W3C IndexedDB specification while running entirely in memory without disk persistence, making it ideal for unit testing, continuous integration, and development scenarios.
npm install --save-dev fake-indexeddbimport "fake-indexeddb/auto";
// Now use indexedDB, IDBDatabase, etc. as globals
const request = indexedDB.open("test", 1);import {
indexedDB,
IDBCursor,
IDBCursorWithValue,
IDBDatabase,
IDBFactory,
IDBIndex,
IDBKeyRange,
IDBObjectStore,
IDBOpenDBRequest,
IDBRequest,
IDBTransaction,
IDBVersionChangeEvent,
} from "fake-indexeddb";const {
indexedDB,
IDBDatabase,
IDBKeyRange,
// ... other exports
} = require("fake-indexeddb");import FDBDatabase from "fake-indexeddb/lib/FDBDatabase";
import FDBKeyRange from "fake-indexeddb/lib/FDBKeyRange";
import forceCloseDatabase from "fake-indexeddb/lib/forceCloseDatabase";import "fake-indexeddb/auto";
// Open database with version
const request = indexedDB.open("testdb", 3);
request.onupgradeneeded = function () {
const db = request.result;
// Create object store with key path
const store = db.createObjectStore("books", { keyPath: "isbn" });
// Create index for searching
store.createIndex("by_title", "title", { unique: true });
// Add initial data
store.put({ title: "JavaScript Guide", author: "Mozilla", isbn: 123456 });
store.put({ title: "TypeScript Handbook", author: "Microsoft", isbn: 234567 });
};
request.onsuccess = function (event) {
const db = event.target.result;
// Start transaction
const tx = db.transaction("books", "readonly");
const store = tx.objectStore("books");
// Query by index
store.index("by_title").get("JavaScript Guide").onsuccess = function (event) {
console.log("Found book:", event.target.result);
};
// Cursor iteration with key range
store.openCursor(IDBKeyRange.lowerBound(200000)).onsuccess = function (event) {
const cursor = event.target.result;
if (cursor) {
console.log("Book:", cursor.value);
cursor.continue();
}
};
tx.oncomplete = function () {
console.log("Transaction completed");
db.close();
};
};fake-indexeddb implements the complete IndexedDB specification with several key components:
IDBFactory (accessed as indexedDB) creates and manages database connectionsIDBDatabase represents database connections with object stores and transactionsIDBObjectStore and IDBIndex provide data storage and indexing capabilitiesIDBTransaction ensures ACID properties and isolationIDBCursor and IDBCursorWithValue enable efficient data iterationCore database lifecycle operations including opening, creating, upgrading, and deleting databases with full version management support.
interface IDBFactory {
open(name: string, version?: number): IDBOpenDBRequest;
deleteDatabase(name: string): IDBOpenDBRequest;
databases(): Promise<Array<{ name: string; version: number }>>;
}
interface IDBDatabase extends EventTarget {
readonly name: string;
readonly version: number;
readonly objectStoreNames: DOMStringList;
createObjectStore(name: string, options?: {
keyPath?: string | string[];
autoIncrement?: boolean;
}): IDBObjectStore;
deleteObjectStore(name: string): void;
transaction(
storeNames: string | string[],
mode?: "readonly" | "readwrite" | "versionchange",
options?: { durability?: "default" | "strict" | "relaxed" }
): IDBTransaction;
close(): void;
}Data storage and retrieval operations with support for primary keys, indexes, and complex queries.
interface IDBObjectStore {
readonly name: string;
readonly keyPath: string | string[] | null;
readonly autoIncrement: boolean;
readonly indexNames: DOMStringList;
readonly transaction: IDBTransaction;
put(value: any, key?: IDBValidKey): IDBRequest;
add(value: any, key?: IDBValidKey): IDBRequest;
delete(key: IDBValidKey | IDBKeyRange): IDBRequest;
get(key: IDBValidKey | IDBKeyRange): IDBRequest;
getAll(query?: IDBValidKey | IDBKeyRange, count?: number): IDBRequest;
count(key?: IDBValidKey | IDBKeyRange): IDBRequest;
clear(): IDBRequest;
createIndex(name: string, keyPath: string | string[], options?: {
unique?: boolean;
multiEntry?: boolean;
}): IDBIndex;
index(name: string): IDBIndex;
deleteIndex(name: string): void;
openCursor(range?: IDBValidKey | IDBKeyRange, direction?: IDBCursorDirection): IDBRequest;
openKeyCursor(range?: IDBValidKey | IDBKeyRange, direction?: IDBCursorDirection): IDBRequest;
}Transaction management and cursor-based iteration for efficient data processing and batch operations.
interface IDBTransaction extends EventTarget {
readonly objectStoreNames: DOMStringList;
readonly mode: "readonly" | "readwrite" | "versionchange";
readonly db: IDBDatabase;
readonly error: DOMException | null;
objectStore(name: string): IDBObjectStore;
abort(): void;
commit(): void;
}
interface IDBCursor {
readonly source: IDBObjectStore | IDBIndex;
readonly direction: IDBCursorDirection;
readonly key: IDBValidKey;
readonly primaryKey: IDBValidKey;
advance(count: number): void;
continue(key?: IDBValidKey): void;
continuePrimaryKey(key: IDBValidKey, primaryKey: IDBValidKey): void;
delete(): IDBRequest;
update(value: any): IDBRequest;
}Advanced querying capabilities with key ranges, bounds, and filtering for precise data selection.
interface IDBKeyRangeStatic {
only(value: IDBValidKey): IDBKeyRange;
lowerBound(lower: IDBValidKey, open?: boolean): IDBKeyRange;
upperBound(upper: IDBValidKey, open?: boolean): IDBKeyRange;
bound(
lower: IDBValidKey,
upper: IDBValidKey,
lowerOpen?: boolean,
upperOpen?: boolean
): IDBKeyRange;
}
interface IDBKeyRange {
readonly lower: IDBValidKey | undefined;
readonly upper: IDBValidKey | undefined;
readonly lowerOpen: boolean;
readonly upperOpen: boolean;
includes(key: IDBValidKey): boolean;
}Event handling, error management, and testing utilities for comprehensive IndexedDB simulation.
interface IDBRequest extends EventTarget {
readonly result: any;
readonly error: DOMException | null;
readonly source: IDBObjectStore | IDBIndex | IDBCursor | null;
readonly transaction: IDBTransaction | null;
readonly readyState: "pending" | "done";
onsuccess: ((event: Event) => void) | null;
onerror: ((event: Event) => void) | null;
}
interface IDBVersionChangeEvent extends Event {
readonly oldVersion: number;
readonly newVersion: number | null;
}
// Testing utility for abnormal database closure
declare function forceCloseDatabase(db: IDBDatabase): void;// Valid IndexedDB key types
type IDBValidKey = number | string | Date | BufferSource | IDBValidKey[];
// Cursor iteration directions
type IDBCursorDirection = "next" | "nextunique" | "prev" | "prevunique";
// Transaction durability options
type IDBTransactionDurability = "default" | "strict" | "relaxed";
// Key path specifications
type IDBKeyPath = string | string[];
// Transaction modes
type IDBTransactionMode = "readonly" | "readwrite" | "versionchange";
// Event callback type
type EventCallback = (event: Event) => void;fake-indexeddb throws standard IndexedDB exceptions:
AbortError - Transaction was abortedConstraintError - Constraint violation (unique index, etc.)DataError - Invalid key or key rangeInvalidAccessError - Invalid operation for current stateInvalidStateError - Operation not allowed in current stateNotFoundError - Requested object not foundReadOnlyError - Write operation on readonly transactionTransactionInactiveError - Transaction is not activeVersionError - Invalid version numberimport { forceCloseDatabase } from "fake-indexeddb";
// Simulate abnormal database closure
db.onclose = () => console.log("Database forcibly closed");
forceCloseDatabase(db); // Triggers close eventfake-indexeddb supports flexible import patterns to accommodate different testing frameworks and project structures, making it easy to integrate with Jest, Mocha, Vitest, and other testing environments.