BigBlocks Docs
Components/Developer Tools

Storage Adapter

Pluggable storage interface for BigBlocks authentication and data persistence

A pluggable storage interface that provides a consistent API for data persistence across different environments. BigBlocks uses storage adapters for authentication data, session management, and backup persistence.

Storage Adapter InterfaceBigBlocks storage adapters provide a consistent async API: get, set, delete, and clear. Try the operations below to see how different storage types work.
StorageAdapter Interface
interface StorageAdapter {
  get: (key: string) => Promise<string | null>;
  set: (key: string, value: string) => Promise<void>;
  delete: (key: string) => Promise<void>;
  clear?: () => Promise<void>;
}
Live Storage Adapters
Memory Storage
Add Entry:
Stored Entries (0):No entries stored
Operations:
No operations yet...
Local Storage (demo:)
Add Entry:
Stored Entries (0):No entries stored
Operations:
No operations yet...

Installation

npx bigblocks add storage-adapter

Import

import { createMemoryStorage, createLocalStorage } from 'bigblocks';

Interface

All storage adapters implement the StorageAdapter interface:

interface StorageAdapter {
  get: (key: string) => Promise<string | null>;
  set: (key: string, value: string) => Promise<void>;
  delete: (key: string) => Promise<void>;
  clear?: () => Promise<void>;
}

Built-in Adapters

Memory Storage

For testing and development without persistence:

import { createMemoryStorage } from 'bigblocks';

const storage = createMemoryStorage();

// Usage with auth
const authManager = createAuthManager({
  apiUrl: '/api',
  storage: storage
});

Local Storage

For browser-based persistence:

import { createLocalStorage } from 'bigblocks';

const storage = createLocalStorage();

// With namespace
const namespacedStorage = createLocalStorage('myapp');

Session Storage

For temporary session-based storage:

import { createSessionStorage } from 'bigblocks';

const storage = createSessionStorage();

Custom Storage Adapters

Server-Side Storage

For Node.js environments:

import { StorageAdapter } from 'bigblocks';

class ServerStorageAdapter implements StorageAdapter {
  private storage = new Map<string, string>();
  
  async get(key: string): Promise<string | null> {
    // Implement database/file system retrieval
    return this.storage.get(key) || null;
  }
  
  async set(key: string, value: string): Promise<void> {
    // Implement database/file system storage
    this.storage.set(key, value);
  }
  
  async delete(key: string): Promise<void> {
    // Implement database/file system removal
    this.storage.delete(key);
  }
  
  async clear(): Promise<void> {
    // Clear all stored data
    this.storage.clear();
  }
}

const auth = createAuthManager({
  apiUrl: '/api',
  storage: new ServerStorageAdapter()
});

Redis Storage

For distributed applications:

import Redis from 'ioredis';
import { StorageAdapter } from 'bigblocks';

class RedisStorageAdapter implements StorageAdapter {
  private redis: Redis;
  private namespace: string;
  
  constructor(redisUrl: string, namespace = 'bigblocks:') {
    this.redis = new Redis(redisUrl);
    this.namespace = namespace;
  }
  
  async get(key: string): Promise<string | null> {
    return this.redis.get(this.namespace + key);
  }
  
  async set(key: string, value: string): Promise<void> {
    await this.redis.set(this.namespace + key, value);
  }
  
  async delete(key: string): Promise<void> {
    await this.redis.del(this.namespace + key);
  }
  
  async clear(): Promise<void> {
    const keys = await this.redis.keys(this.namespace + '*');
    if (keys.length > 0) {
      await this.redis.del(...keys);
    }
  }
}

const auth = createBigBlocksAuth({
  storage: new RedisStorageAdapter(process.env.REDIS_URL!)
});

Encrypted Storage

For enhanced security:

import { StorageAdapter } from 'bigblocks';
import CryptoJS from 'crypto-js';

class EncryptedStorageAdapter implements StorageAdapter {
  private storage: StorageAdapter;
  private encryptionKey: string;
  
  constructor(storage: StorageAdapter, encryptionKey: string) {
    this.storage = storage;
    this.encryptionKey = encryptionKey;
  }
  
  async get(key: string): Promise<string | null> {
    const encrypted = await this.storage.get(key);
    if (!encrypted) return null;
    
    try {
      const bytes = CryptoJS.AES.decrypt(encrypted, this.encryptionKey);
      return bytes.toString(CryptoJS.enc.Utf8);
    } catch {
      return null; // Invalid or corrupted data
    }
  }
  
  async set(key: string, value: string): Promise<void> {
    const encrypted = CryptoJS.AES.encrypt(value, this.encryptionKey).toString();
    await this.storage.set(key, encrypted);
  }
  
  async delete(key: string): Promise<void> {
    await this.storage.delete(key);
  }
  
  async clear(): Promise<void> {
    await this.storage.clear();
  }
}

// Usage
const baseStorage = createLocalStorage();
const encryptedStorage = new EncryptedStorageAdapter(baseStorage, 'secret-key');

const auth = createAuthManager({
  apiUrl: '/api',
  storage: encryptedStorage
});

Usage with Components

AuthManager

import { createAuthManager, createMemoryStorage } from 'bigblocks';

const authManager = createAuthManager({
  apiUrl: '/api',
  storage: createMemoryStorage(), // For testing
  storageNamespace: 'auth:'
});

BitcoinAuthProvider

import { BitcoinAuthProvider, createLocalStorage } from 'bigblocks';

function App() {
  return (
    <BitcoinAuthProvider
      appId="my-app"
      apiUrl="/api"
      storage={createLocalStorage('my-app')}
    >
      {/* Your app */}
    </BitcoinAuthProvider>
  );
}

createBigBlocksAuth

import { createBigBlocksAuth, createSessionStorage } from 'bigblocks';

const auth = createBigBlocksAuth({
  apiUrl: '/api',
  storage: createSessionStorage(),
  storageNamespace: 'bigblocks:auth:'
});

Configuration Options

Storage Namespacing

Use namespaces to avoid key conflicts:

const auth = createAuthManager({
  apiUrl: '/api',
  storage: createLocalStorage(),
  storageNamespace: 'myapp:auth:' // Prefix for all keys
});

Multiple Storage Instances

Use different storage for different purposes:

const sessionStorage = createSessionStorage();
const localStorage = createLocalStorage();

// Session data
const auth = createAuthManager({
  apiUrl: '/api',
  storage: sessionStorage
});

// Persistent user preferences
const preferences = {
  storage: localStorage,
  get: (key: string) => localStorage.get(`prefs:${key}`),
  set: (key: string, value: string) => localStorage.set(`prefs:${key}`, value)
};

Testing

Mock Storage

For unit tests:

import { StorageAdapter } from 'bigblocks';

class MockStorageAdapter implements StorageAdapter {
  private data: Record<string, string> = {};
  
  async get(key: string): Promise<string | null> {
    return this.data[key] || null;
  }
  
  async set(key: string, value: string): Promise<void> {
    this.data[key] = value;
  }
  
  async delete(key: string): Promise<void> {
    delete this.data[key];
  }
  
  async clear(): Promise<void> {
    this.data = {};
  }
  
  // Test helpers
  getData() {
    return { ...this.data };
  }
  
  setData(data: Record<string, string>) {
    this.data = { ...data };
  }
}

// In tests
const mockStorage = new MockStorageAdapter();
const auth = createAuthManager({
  apiUrl: '/api',
  storage: mockStorage
});

// Verify storage calls
expect(mockStorage.getData()).toEqual({
  'auth:session': 'encrypted-session-data'
});

Error Handling

Graceful Degradation

class FallbackStorageAdapter implements StorageAdapter {
  private primary: StorageAdapter;
  private fallback: StorageAdapter;
  
  constructor(primary: StorageAdapter, fallback: StorageAdapter) {
    this.primary = primary;
    this.fallback = fallback;
  }
  
  async get(key: string): Promise<string | null> {
    try {
      return await this.primary.get(key);
    } catch {
      return await this.fallback.get(key);
    }
  }
  
  async set(key: string, value: string): Promise<void> {
    try {
      await this.primary.set(key, value);
    } catch {
      await this.fallback.set(key, value);
    }
  }
  
  async delete(key: string): Promise<void> {
    await Promise.all([
      this.primary.delete(key).catch(() => {}),
      this.fallback.delete(key).catch(() => {})
    ]);
  }
  
  async clear(): Promise<void> {
    await Promise.all([
      this.primary.clear().catch(() => {}),
      this.fallback.clear().catch(() => {})
    ]);
  }
}

// Usage
const storage = new FallbackStorageAdapter(
  createLocalStorage(),
  createMemoryStorage()
);

Storage Quota Management

class QuotaAwareStorage implements StorageAdapter {
  private storage: StorageAdapter;
  private maxSize: number;
  
  constructor(storage: StorageAdapter, maxSize = 5 * 1024 * 1024) { // 5MB
    this.storage = storage;
    this.maxSize = maxSize;
  }
  
  async setItem(key: string, value: string): Promise<void> {
    if (value.length > this.maxSize) {
      throw new Error('Value exceeds storage quota');
    }
    
    try {
      await this.storage.set(key, value);
    } catch (error) {
      if (error.name === 'QuotaExceededError') {
        // Clear old data and retry
        await this.cleanupOldData();
        await this.storage.set(key, value);
      } else {
        throw error;
      }
    }
  }
  
  private async cleanupOldData(): Promise<void> {
    // Implement cleanup strategy
    console.warn('Storage quota exceeded, cleaning up old data');
  }
  
  async get(key: string): Promise<string | null> {
    return this.storage.get(key);
  }
  
  async delete(key: string): Promise<void> {
    return this.storage.delete(key);
  }
  
  async clear(): Promise<void> {
    return this.storage.clear();
  }
}

Best Practices

  1. Choose the Right Adapter: Use memory storage for testing, local storage for browsers, custom adapters for servers
  2. Handle Async Operations: Many storage operations are async, always await them
  3. Use Namespaces: Prevent key conflicts with proper namespacing
  4. Error Handling: Implement fallback strategies for storage failures
  5. Security: Use encrypted storage for sensitive data
  6. Testing: Mock storage for unit tests to avoid side effects

Common Patterns

Configuration Factory

function createStorageConfig(env: 'development' | 'test' | 'production') {
  switch (env) {
    case 'test':
      return createMemoryStorage();
    
    case 'development':
      return createLocalStorage('dev');
    
    case 'production':
      return new EncryptedStorageAdapter(
        createLocalStorage(),
        process.env.ENCRYPTION_KEY!
      );
  }
}

const storage = createStorageConfig(process.env.NODE_ENV);

Multi-Layer Storage

class CachedStorageAdapter implements StorageAdapter {
  private cache: StorageAdapter;
  private persistent: StorageAdapter;
  
  constructor(cache: StorageAdapter, persistent: StorageAdapter) {
    this.cache = cache;
    this.persistent = persistent;
  }
  
  async get(key: string): Promise<string | null> {
    // Try cache first
    let value = await this.cache.get(key);
    if (value === null) {
      // Fallback to persistent storage
      value = await this.persistent.get(key);
      if (value !== null) {
        // Cache for next time
        await this.cache.set(key, value);
      }
    }
    return value;
  }
  
  async set(key: string, value: string): Promise<void> {
    await Promise.all([
      this.cache.set(key, value),
      this.persistent.set(key, value)
    ]);
  }
  
  async delete(key: string): Promise<void> {
    await Promise.all([
      this.cache.delete(key),
      this.persistent.delete(key)
    ]);
  }
  
  async clear(): Promise<void> {
    await Promise.all([
      this.cache.clear(),
      this.persistent.clear()
    ]);
  }
}

API Reference

This component provides a consistent storage interface for all BigBlocks components and utilities. The interface supports both synchronous and asynchronous operations to accommodate different storage backends.

Core Interface

interface StorageAdapter {
  get: (key: string) => Promise<string | null>;
  set: (key: string, value: string) => Promise<void>;
  delete: (key: string) => Promise<void>;
  clear?: () => Promise<void>;
}

Built-in Utilities

  • createMemoryStorage() - In-memory storage for testing
  • createLocalStorage(namespace?) - Browser localStorage wrapper
  • createSessionStorage(namespace?) - Browser sessionStorage wrapper