Storage
Persistent storage with IndexedDB and memory fallbacks.
LocalMode provides flexible storage options for persisting vector databases and application data.
Storage Options
The default storage uses IndexedDB for persistence:
import { IndexedDBStorage, createVectorDB } from '@localmode/core';
const storage = new IndexedDBStorage({
name: 'my-app',
storeName: 'vectors', // Optional, defaults to 'store'
});
// Or use default (IndexedDB) automatically:
const db = await createVectorDB({
name: 'documents',
dimensions: 384,
// Uses IndexedDBStorage by default
});Data persists across page reloads and browser restarts.
For temporary data or environments without IndexedDB:
import { MemoryStorage, createVectorDB } from '@localmode/core';
const db = await createVectorDB({
name: 'temp',
dimensions: 384,
storage: new MemoryStorage(),
});
// ⚠️ Data is lost on page reloadUseful for:
- Testing and development
- Temporary caches
- Safari private browsing fallback
Safari's private browsing mode blocks IndexedDB. Use MemoryStorage as a fallback or detect this
condition with isIndexedDBSupported().
Storage Interface
All storage adapters implement this interface:
Prop
Type
StoredDocument
Prop
Type
Third-Party Adapters
Dexie.js
import { DexieStorage } from '@localmode/dexie';
import { createVectorDB } from '@localmode/core';
const db = await createVectorDB({
name: 'dexie-db',
dimensions: 384,
storage: new DexieStorage({
name: 'my-app',
version: 1,
}),
});idb
import { IDBStorage } from '@localmode/idb';
import { createVectorDB } from '@localmode/core';
const db = await createVectorDB({
name: 'idb-db',
dimensions: 384,
storage: new IDBStorage({
name: 'my-app',
}),
});localForage
import { LocalForageStorage } from '@localmode/localforage';
import { createVectorDB } from '@localmode/core';
const db = await createVectorDB({
name: 'lf-db',
dimensions: 384,
storage: new LocalForageStorage({
name: 'my-app',
driver: 'INDEXEDDB',
}),
});Custom Storage
Implement your own storage adapter:
import type { Storage, StoredDocument } from '@localmode/core';
class MyCustomStorage implements Storage {
private data = new Map<string, StoredDocument>();
async get(key: string) {
return this.data.get(key);
}
async set(key: string, value: StoredDocument) {
this.data.set(key, value);
}
async delete(key: string) {
this.data.delete(key);
}
async keys() {
return Array.from(this.data.keys());
}
async clear() {
this.data.clear();
}
async close() {
// Cleanup if needed
}
}Storage Fallback
Automatically fallback when IndexedDB is unavailable:
import { createStorageWithFallback, IndexedDBStorage, MemoryStorage } from '@localmode/core';
const storage = await createStorageWithFallback({
providers: [() => new IndexedDBStorage({ name: 'app' }), () => new MemoryStorage()],
onFallback: (error, index) => {
console.warn(`Storage provider ${index} failed:`, error.message);
},
});
const db = await createVectorDB({
name: 'robust-db',
dimensions: 384,
storage,
});Quota Management
Monitor and manage storage quota:
import { getStorageQuota, requestPersistence } from '@localmode/core';
// Check available quota
const quota = await getStorageQuota();
console.log('Used:', quota.usage);
console.log('Available:', quota.quota);
console.log('Percent used:', ((quota.usage / quota.quota) * 100).toFixed(1) + '%');
// Request persistent storage (won't be auto-cleared)
const isPersisted = await requestPersistence();
if (isPersisted) {
console.log('Storage is now persistent');
}Quota Warnings
import { checkQuotaWithWarnings } from '@localmode/core';
const { ok, warning, quota } = await checkQuotaWithWarnings({
warningThreshold: 0.8, // Warn at 80% usage
});
if (warning) {
console.warn('Storage is almost full!', quota);
}Cleanup
Remove old or unused data:
import { cleanup } from '@localmode/core';
// Clean up databases older than 30 days
await cleanup({
maxAge: 30 * 24 * 60 * 60 * 1000, // 30 days in ms
onDelete: (name) => console.log(`Deleted: ${name}`),
});
// Clean up to free space
await cleanup({
targetFreeSpace: 100 * 1024 * 1024, // 100MB
});Cross-Tab Synchronization
Keep data in sync across browser tabs:
import { createBroadcaster } from '@localmode/core';
const broadcaster = createBroadcaster('my-app-sync');
// Listen for changes from other tabs
broadcaster.subscribe((message) => {
if (message.type === 'document-added') {
console.log('New document added in another tab:', message.id);
// Refresh your UI
}
});
// Broadcast changes to other tabs
await db.add({ id: 'new-doc', vector, metadata });
broadcaster.publish({
type: 'document-added',
id: 'new-doc',
});Web Locks
Prevent concurrent writes:
import { createLockManager } from '@localmode/core';
const locks = createLockManager();
// Acquire exclusive lock before writing
await locks.withLock('db-write', async () => {
await db.addMany(documents);
});
// Other tabs wait for lock to be releasedFeature Detection
Check storage capabilities:
import { isIndexedDBSupported, isWebLocksSupported } from '@localmode/core';
if (!isIndexedDBSupported()) {
console.warn('IndexedDB not available, using memory storage');
}
if (!isWebLocksSupported()) {
console.warn('Web Locks not available, using fallback');
}Best Practices
Storage Tips
- Always use fallbacks - Safari private browsing blocks IndexedDB
- Request persistence - Prevent auto-clearing of important data
- Monitor quota - Show warnings before storage is full
- Clean up - Remove old data periodically
- Use locks - Prevent race conditions across tabs