LocalMode
Dexie

Overview

Enhanced IndexedDB storage with Dexie.js — schema versioning and transactions.

@localmode/dexie

Enhanced IndexedDB storage adapter using Dexie.js. Provides schema versioning, compound indexes, and transactional operations for VectorDB storage.

Features

  • 🗄️ Schema Versioning — Automatic database migrations with Dexie.js
  • 🔄 Transactions — Atomic operations across multiple object stores
  • 📇 Compound Indexes — Efficient queries on collection-scoped data
  • 📦 ~15KB — Lightweight Dexie.js dependency

Installation

bash pnpm install @localmode/dexie @localmode/core
bash npm install @localmode/dexie @localmode/core
bash yarn add @localmode/dexie @localmode/core
bash bun add @localmode/dexie @localmode/core

Quick Start

import { DexieStorage } from '@localmode/dexie';
import { createVectorDB, embed, ingest } from '@localmode/core';
import { transformers } from '@localmode/transformers';

// Create storage
const storage = new DexieStorage({ name: 'my-app' });

// Use with VectorDB
const db = await createVectorDB({
  name: 'documents',
  dimensions: 384,
  storage,
});

// Ingest documents
const model = transformers.embedding('Xenova/bge-small-en-v1.5');

await ingest({
  db,
  model,
  documents: [
    { text: 'Local-first AI is the future', metadata: { source: 'blog' } },
    { text: 'Privacy-preserving machine learning', metadata: { source: 'paper' } },
  ],
});

The ingest() function is part of the RAG pipeline. It chunks, embeds, and stores documents in a single call.

Configuration

Prop

Type

Why Dexie?

Dexie.js wraps IndexedDB with a cleaner API and adds features that are useful for production applications:

  • Schema versioning — Define table schemas declaratively with automatic migrations
  • Transactions — Atomic operations ensure data consistency (e.g., clearing a collection deletes documents, vectors, and indexes atomically)
  • Error handling — Better error messages than raw IndexedDB
  • Compound queries — Efficient where('collectionId').equals(id) lookups via indexes

Transaction Support

DexieStorage uses Dexie's built-in transactions for atomic operations. When clearing a collection or the entire database, all related stores are updated atomically — if any step fails, the entire operation rolls back:

// clearCollection() atomically deletes documents, vectors, and index for a collection
await db.clear('my-collection');

// clear() atomically clears all four stores: documents, vectors, indexes, collections

This prevents data corruption from partial writes (e.g., documents deleted but vectors left orphaned).

Persistence Guarantees

DexieStorage round-trips the full Collection object, so everything core stores on it survives a page reload or database reopen: SQ8/PQ vector quantization calibration (calibration, pqCodebook), storage-compression calibration (compressionCalibration, deltaCalibration, compression), and the embedding-drift modelFingerprint. Quantized and compressed databases decode identically across sessions, and drift detection works the same as with core's built-in IndexedDBStorage.

Used quantization or compression before this fix?

Earlier versions of this adapter persisted only four Collection fields and silently dropped the calibration data — quantized/compressed vectors round-tripped fine in-session but decoded as raw bytes after a reopen. The calibration was never stored, so affected databases cannot be repaired: clear and re-ingest (databases that never used quantization or compression are unaffected).

// Recover a database that used quantization/compression before the fix
await db.clear(); // or delete the underlying database: indexedDB.deleteDatabase('my-app')

// Then re-ingest from your source documents
await ingest({ db, model, documents });

Writing your own adapter? Verify the same guarantees with the StorageAdapter conformance suite — the official adapters run it in their own test suites.

Storage Fallback

Gracefully fallback when IndexedDB is unavailable (e.g., Safari private browsing):

import { MemoryStorage } from '@localmode/core';
import { DexieStorage } from '@localmode/dexie';

let storage: DexieStorage | MemoryStorage;

try {
  storage = new DexieStorage({ name: 'my-app' });
  await storage.open();
} catch (error) {
  console.warn('DexieStorage unavailable, falling back to memory:', error);
  storage = new MemoryStorage();
}

Comparison

AdapterPackageBundle SizeTransactionsAuto-FallbackBest For
IndexedDBStorage@localmode/core0KB (built-in)NoNoSimple apps, zero extra deps
DexieStorage@localmode/dexie~15KBYesNoProduction apps needing schema versioning
IDBStorage@localmode/idb~3KBNoNoMinimal bundle size
LocalForageStorage@localmode/localforage~10KBNoYesMax browser compatibility

Next Steps

On this page