mrkeyoor.com_
Sat 08 Aug 21:02 UTC
npmTestingupdated 08 Aug 2026

fake-indexeddb

fake-indexeddb is a dependency-free, in-memory JavaScript implementation of the browser IndexedDB API, built mainly for unit tests that run in Node. It supplies indexedDB plus the standard IDB classes, supports both automatic global installation and explicit imports, and works with wrappers such as Dexie and idb. Its data lasts only inside the current JavaScript process and factory instance. This is a test double for storage behavior, not a persistent database and not a complete browser environment.

Verdict

Use fake-indexeddb for fast unit coverage around code that speaks IndexedDB, especially when dependencies can be injected. Keep real-browser tests for browser scheduling, storage policy, and the conformance gaps its own README measures.

API stability4/5Most of the public surface mirrors the IndexedDB web standard, so application code uses familiar IDBFactory, IDBDatabase, request, transaction, key-range, index, and cursor APIs rather than a private abstraction. Version 6 also exports both ESM and CommonJS entry points. Major releases have still changed environmental assumptions: version 5 removed the structuredClone polyfill, version 3 removed broader core-js polyfills, and version 6 requires Node 18 or newer.
Docs5/5The README covers automatic and explicit imports, TypeScript, Dexie injection, Jest setup, jsdom structuredClone fixes, state reset, abnormal close simulation, and support for old environments. It also publishes a dated Web Platform Test comparison with exact pass counts and explains omitted browser-only cases. That combination gives users both copyable setup and an unusually candid boundary around fidelity.
Maintenance5/5Version 6.2.5 was published in November 2025, the repository was pushed on May 13, 2026, and GitHub reports only 7 open issues and pull requests. The project continuously compares itself with the IndexedDB Web Platform Tests; the current README records 1,369 passing cases out of 1,653. That test discipline is directly relevant to a standards emulation package and provides stronger evidence than release frequency alone.
Ecosystem4/5The package recorded 5,086,965 downloads for the fetched week and explicitly documents Jest, jsdom, Dexie, and idb integration. It has no runtime dependencies, includes TypeScript declarations, and offers ESM plus CommonJS exports. The ecosystem score stops short of five because it intentionally supplies only IndexedDB, not a full browser environment, and wrappers still need import-order or dependency-injection setup.

Use it if

  • You unit-test code that calls IndexedDB but want tests to run quickly in Node without launching a browser
  • You need a fresh, isolated IDBFactory for each test or suite so database names and records cannot leak between cases
  • You use Dexie or idb and want to inject an IndexedDB-compatible backend in tests
  • You need to exercise upgrades, object stores, indexes, cursors, transactions, and abnormal close handling in memory
Skip it if

Setup reality

Install it as a development dependency. The easiest setup is importing fake-indexeddb/auto before application code; that mutates the global scope with indexedDB and all IDB constructors, so import order matters for wrappers that capture globals at module load time. Dexie must see the auto import first, or receive indexedDB and IDBKeyRange explicitly in its constructor. For Jest, put fake-indexeddb/auto in setupFiles rather than setupFilesAfterEnv when modules need the globals during import. Version 6 supports both ESM imports and CommonJS require through explicit export mappings and includes TypeScript declarations based on TypeScript's built-in IndexedDB types. The package requires Node 18 or newer. Since version 5 it does not ship a structuredClone polyfill. Node itself has structuredClone, but jsdom can expose a separate global that lacks it; the documented fixes are to import core-js/stable/structured-clone before fake-indexeddb or copy Node's implementation into a custom Jest environment. State is shared for as long as one IDBFactory lives. Closing database connections does not erase databases, so test isolation means calling deleteDatabase and waiting for success or replacing the factory with new IDBFactory(). Outstanding connections can block upgrades and deletion just as they do in browsers, which means cleanup must close handles. Transactions and requests remain event-based, so helper promises must listen for error, abort, and complete rather than assuming a successful request means the entire transaction committed. forceCloseDatabase exists for close-event coverage, but it is a package-specific testing extension and must never enter browser production code. Finally, conformance is good but incomplete: keep at least a small real-browser test layer for upgrade races, quota, workers, and any bug that depends on browser task scheduling.

Patterns

Install IndexedDB globals for a testinstall-global-api

import 'fake-indexeddb/auto'

const request = indexedDB.open('app-test', 1)

Import this before modules that read indexedDB at module initialization. It mutates globalThis for the rest of the test environment.

Use an isolated factory without globalsuse-explicit-factory

import {IDBFactory, IDBKeyRange} from 'fake-indexeddb'

const testIndexedDB = new IDBFactory()
const request = testIndexedDB.open('isolated', 1)
console.log(IDBKeyRange.only('active'))

A separate IDBFactory owns separate in-memory databases. Explicit injection avoids cross-test global state.

Create stores and indexes during upgradecreate-database-schema

import {indexedDB} from 'fake-indexeddb'

const request = indexedDB.open('library', 1)
request.onupgradeneeded = () => {
  const store = request.result.createObjectStore('books', {keyPath: 'isbn'})
  store.createIndex('by_title', 'title', {unique: true})
}
request.onsuccess = () => console.log('opened', request.result.name)

Schema changes belong in onupgradeneeded. Keep older connections closed or later version upgrades can be blocked.

Turn an IDB request into a promiseawait-idb-request

function requestToPromise(request) {
  return new Promise((resolve, reject) => {
    request.onsuccess = () => resolve(request.result)
    request.onerror = () => reject(request.error)
  })
}

const db = await requestToPromise(indexedDB.open('library', 1))

This helper waits for one request only. A successful write request can precede the enclosing transaction's final commit.

Write a record and await transaction commitwrite-and-commit

const tx = db.transaction('books', 'readwrite')
const store = tx.objectStore('books')
store.put({isbn: '978-1', title: 'The Test Book'})

await new Promise((resolve, reject) => {
  tx.oncomplete = resolve
  tx.onerror = () => reject(tx.error)
  tx.onabort = () => reject(tx.error ?? new Error('transaction aborted'))
})

Wait for tx.oncomplete when the test cares that all writes committed. Waiting only for put().onsuccess is too early.

Read a record through an indexquery-index

const tx = db.transaction('books', 'readonly')
const request = tx.objectStore('books').index('by_title').get('The Test Book')
const book = await requestToPromise(request)
console.log(book.isbn)

Index names and uniqueness behave like IndexedDB, including constraint errors, but keep a real-browser test for edge-case conformance.

Iterate records with a bounded cursoriterate-cursor

import {IDBKeyRange} from 'fake-indexeddb'

const store = db.transaction('books').objectStore('books')
const request = store.openCursor(IDBKeyRange.lowerBound('978-1'))
request.onsuccess = () => {
  const cursor = request.result
  if (!cursor) return
  console.log(cursor.key, cursor.value)
  cursor.continue()
}

Cursor iteration is event-driven. The request fires success again after each continue() and once more with a null result at the end.

Replace state between test casesreset-between-tests

import {IDBFactory} from 'fake-indexeddb'

let testIndexedDB

beforeEach(() => {
  testIndexedDB = new IDBFactory()
})

Replacing the factory is the simplest full reset. Application code must receive this instance rather than retaining a previously imported global.

Install globals for every Jest testconfigure-jest

export default {
  setupFiles: ['fake-indexeddb/auto'],
  testEnvironment: 'node'
}

Use setupFiles so globals exist before test modules load. A jsdom environment may also need a structuredClone fix.

Provide structuredClone in jsdompolyfill-structured-clone

import 'core-js/stable/structured-clone'
import 'fake-indexeddb/auto'

Import order matters. Since fake-indexeddb 5, the package does not include this polyfill; core-js is a separate dependency.

Use Dexie without changing globalsinject-into-dexie

import Dexie from 'dexie'
import {indexedDB, IDBKeyRange} from 'fake-indexeddb'

const db = new Dexie('MyDatabase', {indexedDB, IDBKeyRange})
db.version(1).stores({friends: '++id,name'})

Explicit injection is safer for parallel tests. If you use fake-indexeddb/auto instead, import it before Dexie.

Trigger an abnormal close eventsimulate-abnormal-close

import {forceCloseDatabase} from 'fake-indexeddb'

const closed = new Promise((resolve) => db.addEventListener('close', resolve, {once: true}))
forceCloseDatabase(db)
await closed

forceCloseDatabase() is a fake-indexeddb testing extension, not a standard IndexedDB API and not portable to browsers.

Alternatives

PackageRegistryPick it when
indexeddbshimnpmYou need an IndexedDB shim for environments backed by another storage mechanism rather than a Node-only in-memory test double
@web/test-runnernpmYou want unit-style tests executed in real browsers so the native IndexedDB implementation is part of the test
playwrightnpmYou need end-to-end coverage of persistence, browser profiles, workers, or upgrade behavior in Chromium, Firefox, and WebKit
memory-levelnpmYou only need an in-memory key-value database for Node tests and do not need the IndexedDB API contract