Skip to main content

Storage Adapters

Strata Storage provides multiple storage adapters to work across different platforms and use cases.

Adapter Overview

AdapterPlatformPersistenceCapacityPerformanceBest For
MemoryAll❌ No~50MB⚡ FastTemporary data, caching
LocalStorageWeb✅ Yes~10MB🚀 FastSmall persistent data
SessionStorageWeb🔄 Session~10MB🚀 FastSession data
IndexedDBWeb✅ Yes~1GB+⚡ FastLarge datasets
CookiesWeb✅ Yes~4KB🐌 SlowSmall server-shared data
CacheWeb✅ Yes~500MB+⚡ FastOffline resources
URLWeb❌ No~2KB🚀 FastShareable, reload-surviving UI state
PreferencesMobile✅ Yes~1MB🚀 FastApp settings
SQLiteiOS/Android✅ YesUnlimited⚡ FastStructured data, multi-store isolation (v2.6.0)
SecureMobile✅ Yes~5MB🔒 SecureSensitive data
FilesystemiOS/Android✅ YesDevice storage📁 Variable (file I/O)Large or document-shaped values (v2.6.0: native backend added)

Remote / sync adapters (opt-in)

These are not auto-selected; you register them explicitly (via enableFirebaseSync) and target them with { storage: '...' }. firebase is an optional peer dependency.

AdapterBackendObservableQueryableBest For
firestoreFirebase Cloud FirestoreCross-device/user document sync
realtimeFirebase Realtime DatabaseLow-latency shared state

Adapter Selection

Strata automatically selects the best available adapter based on:

  1. Platform - Web, iOS, Android, or Node.js
  2. Availability - Whether the adapter is supported
  3. Configuration - Your specified preferences
  4. Strategy - Performance, reliability, or capacity first

Automatic Selection

// Strata will automatically choose the best adapter
const storage = new Strata();
await storage.initialize();

// Will use IndexedDB on web, SQLite on mobile
await storage.set('data', largeObject);

Manual Selection

// Force specific storage adapter
await storage.set('data', value, {
storage: 'localStorage'
});

// Try multiple adapters in order
await storage.set('data', value, {
storage: ['secure', 'preferences', 'memory']
});

Adapter Capabilities

Each adapter has different capabilities:

interface StorageCapabilities {
persistent: boolean; // Survives across sessions
synchronous: boolean; // Supports synchronous operations
observable: boolean; // Supports subscriptions/watching
transactional: boolean; // Supports transactions
queryable: boolean; // Supports querying
maxSize: number; // Maximum storage size (bytes, -1 for unlimited)
binary: boolean; // Supports binary data
encrypted: boolean; // Supports encryption
crossTab: boolean; // Cross-tab/window support
}

Checking Capabilities

// Get capabilities for a specific adapter
const capabilities = storage.getCapabilities('indexedDB');
console.log('Supports transactions:', capabilities.transactional);

// Get all adapter capabilities
const allCapabilities = storage.getCapabilities();

Web Adapters

Memory Adapter

  • In-memory storage using Map
  • Fastest performance
  • No persistence
  • Perfect for temporary data

LocalStorage Adapter

  • Browser's localStorage API
  • Synchronous operations
  • ~10MB capacity
  • Domain-specific persistence

SessionStorage Adapter

  • Browser's sessionStorage API
  • Session-based persistence
  • ~10MB capacity
  • Tab-specific storage

IndexedDB Adapter

  • Browser's IndexedDB API
  • Asynchronous operations
  • Large capacity (GB+)
  • Supports transactions and indexes
  • HTTP cookies
  • Server-accessible
  • ~4KB per cookie
  • Cross-subdomain support

Cache Adapter

  • Service Worker Cache API
  • Network request caching
  • Large capacity
  • Offline support

URL Adapter

  • Stores state in the page URL (query or hash)
  • Synchronous; emits change events on navigation
  • Shareable and reload-surviving
  • For small UI state only (~2KB), browser-only, not durable

Mobile Adapters (Capacitor)

Preferences Adapter

  • Native preferences APIs
  • UserDefaults (iOS) / SharedPreferences (Android)
  • Key-value storage
  • App settings and small data

SQLite Adapter

  • SQLite database on iOS (SQLite3) and Android (SQLiteDatabase)
  • Full SQL support; complex queries and indexed fields
  • Multi-store (v2.6.0): database and table options now route to distinct physical database files/tables on iOS and Android; separate defineStorage instances with different database/table values are fully isolated
  • size(true) returns { keys, values, metadata } byte breakdown (v2.6.0)
  • Full StorageValue wrapper (TTL, tags, metadata) round-trips correctly through native get/set (fixed in v2.6.0)
  • Pending on-device verification for v2.6.0 changes; see device-verification guide

Secure Adapter

  • Keychain (iOS) / EncryptedSharedPreferences (Android)
  • Hardware-backed encryption
  • Biometric protection support
  • Sensitive data storage

Filesystem Adapter

  • Native file system (iOS NSDocumentsDirectory, Android getFilesDir())
  • One JSON file per key under strata_storage/, atomic writes via staging rename
  • isAvailable() returns true on iOS/Android; false in browser
  • size(true) returns { keys, values, metadata } byte breakdown
  • v2.6.0: native backend added — previously unavailable on all platforms
  • Pending on-device verification; see device-verification guide

Custom Adapters

You can create custom adapters by extending the BaseAdapter:

import { BaseAdapter, StorageValue, StorageCapabilities } from 'strata-storage';

export class CustomAdapter extends BaseAdapter {
readonly name = 'custom' as const;
readonly capabilities: StorageCapabilities = {
persistent: true,
synchronous: false,
observable: true,
transactional: false,
queryable: false,
maxSize: -1,
binary: false,
encrypted: false,
crossTab: false
};

async initialize(config?: unknown): Promise<void> {
// Initialize your adapter
}

async isAvailable(): Promise<boolean> {
// Check if adapter can be used
return true;
}

async get<T>(key: string): Promise<StorageValue<T> | null> {
// Implement get logic
}

async set<T>(key: string, value: StorageValue<T>): Promise<void> {
// Implement set logic
}

// ... implement other required methods
}

Registering Custom Adapter

import { AdapterRegistry } from 'strata-storage';

const registry = new AdapterRegistry();
registry.register(new CustomAdapter());

Adapter-Specific Configuration

Each adapter can have specific configuration:

const storage = new Strata({
adapters: {
indexedDB: {
name: 'MyAppDB',
version: 1,
stores: ['data', 'cache']
},
localStorage: {
prefix: 'myapp_'
},
cookies: {
domain: '.example.com',
secure: true
},
sqlite: {
database: 'app.db',
version: '1.0'
}
}
});

Performance Comparison

OperationMemoryLocalStorageIndexedDBSQLite
Write 1KB<1ms2-5ms5-10ms5-15ms
Read 1KB<1ms1-2ms5-10ms5-15ms
Write 1MB1ms20-50ms10-20ms20-30ms
Read 1MB<1ms10-20ms10-20ms15-25ms
Query 1000 items1-2msN/A10-20ms5-10ms

Storage Limits

AdapterLimit per ItemTotal LimitNotes
MemoryNo limit~50-100MBLimited by available RAM
LocalStorageNo limit~10MBVaries by browser
SessionStorageNo limit~10MBPer tab/window
IndexedDBNo limit50%+ diskBrowser specific
Cookies4KB50 cookiesPer domain
CacheNo limitVariesBased on available space
Preferences1MB~5MBPlatform specific
SQLiteNo limitUnlimitedLimited by device storage
Secure5KB~5MBPlatform specific
FilesystemNo limitUnlimitedLimited by device storage

Best Practices

1. Choose the Right Adapter

// User preferences - use secure storage
await storage.set('apiKey', key, { storage: 'secure' });

// Large datasets - use IndexedDB or SQLite
await storage.set('dataset', data, { storage: ['sqlite', 'indexedDB'] });

// Temporary data - use memory
await storage.set('cache', data, { storage: 'memory' });

// Session data - use sessionStorage
await storage.set('session', data, { storage: 'sessionStorage' });

2. Handle Adapter Failures

// Fallback chain
const fallbackStorages: StorageType[] = ['indexedDB', 'localStorage', 'memory'];

try {
await storage.set('data', value, { storage: fallbackStorages });
} catch (error) {
console.error('All storage adapters failed');
}

3. Optimize for Platform

// Platform-specific optimization
const storage = new Strata({
defaultStorages: isWeb()
? ['indexedDB', 'localStorage']
: ['sqlite', 'preferences']
});

Next Steps