Skip to main content

Storage Providers API Reference

Storage providers handle persistent data storage for the Vaultys Peer SDK. The SDK includes multiple storage providers optimized for different environments and use cases.

Table of Contents​

Overview​

Storage providers implement a common interface for storing and retrieving data. They handle:

  • Contact information
  • Message history
  • Peer metadata
  • Configuration data
  • File attachments

Available Providers​

ProviderEnvironmentCapacityPerformancePersistence
BrowserStorageProviderBrowserAuto-detectAuto-detectYes
OPFSStorageProviderBrowserGB+ExcellentYes
IndexedDBStorageProviderBrowser~50% diskGoodYes
NodeFSStorageProviderNode.jsDisk spaceExcellentYes
MemoryStorageProviderAnyRAMExcellentNo

Common Interface​

All storage providers implement the StorageProvider interface:

interface StorageProvider {
initialize(config?: StorageConfig): Promise<void>;
read(filePath: string): Promise<Buffer | null>;
write(filePath: string, data: Buffer | Uint8Array | string): Promise<void>;
delete(filePath: string): Promise<void>;
exists(filePath: string): Promise<boolean>;
list(dirPath: string): Promise<string[]>;
createDirectory(dirPath: string): Promise<void>;
getMetadata(filePath: string): Promise<StorageMetadata | null>;
}

StorageConfig​

interface StorageConfig {
rootPath?: string; // Root directory/namespace for storage
[key: string]: any; // Provider-specific options
}

StorageMetadata​

interface StorageMetadata {
size: number;
created: Date;
modified: Date;
isDirectory: boolean;
}

Browser Storage Providers​

BrowserStorageProvider​

Automatically selects the best available storage backend for the browser environment.

Constructor​

new BrowserStorageProvider(preferredBackend?: 'OPFS' | 'IndexedDB' | 'LocalStorage' | 'auto')

Features​

  • Auto-detection: Automatically selects OPFS β†’ IndexedDB β†’ LocalStorage
  • Fallback mechanism: Falls back to next option if preferred backend fails
  • Storage info: Provides quota and usage information
  • Unified API: Same interface regardless of backend

Example​

import { BrowserStorageProvider } from '@vaultys/peer-sdk';

// Auto-detect best backend
const storage = new BrowserStorageProvider();
await storage.initialize();
console.log(`Using backend: ${storage.getBackendType()}`);

// Force specific backend
const indexedDBStorage = new BrowserStorageProvider('IndexedDB');
await indexedDBStorage.initialize();

// Get storage information
const info = await storage.getStorageInfo();
console.log(`Storage usage: ${info.usage} / ${info.quota} bytes`);
console.log(`Percent used: ${info.percentUsed}%`);

// Use storage
await storage.write('config.json', JSON.stringify({ theme: 'dark' }));
const data = await storage.read('config.json');
const config = JSON.parse(data.toString());

Additional Methods​

getBackendType(): string | null;
getStorageInfo(): Promise<{
backend: string | null;
usage?: number;
quota?: number;
percentUsed?: number;
}>;
clear(): Promise<void>;

OPFSStorageProvider​

Uses the Origin Private File System API for high-performance native file system access.

Constructor​

new OPFSStorageProvider()

Requirements​

  • Modern browsers (Chrome 86+, Edge 86+, Opera 72+)
  • HTTPS context
  • Not available in Firefox or Safari (as of 2024)

Features​

  • Native file system: Real file and directory operations
  • Best performance: Direct file system access
  • Large capacity: Gigabytes of storage
  • Persistence control: Request persistent storage

Example​

import { OPFSStorageProvider } from '@vaultys/peer-sdk';

const storage = new OPFSStorageProvider();
await storage.initialize();

// Request persistent storage
const isPersistent = await storage.requestPersistence();
console.log(`Storage persistence: ${isPersistent ? 'granted' : 'denied'}`);

// Check storage info
const info = await storage.getStorageInfo();
console.log(`Available space: ${info.quota - info.usage} bytes`);

// Create directories
await storage.createDirectory('messages/sent');
await storage.createDirectory('messages/received');

// Store files
await storage.write('messages/sent/msg1.json', JSON.stringify({
content: 'Hello',
timestamp: Date.now()
}));

// List files
const files = await storage.list('messages/sent');
console.log('Sent messages:', files);

Additional Methods​

clear(): Promise<void>;
getStorageInfo(): Promise<{
usage: number;
quota: number;
percentUsed: number;
}>;
requestPersistence(): Promise<boolean>;
isPersistent(): Promise<boolean>;

IndexedDBStorageProvider​

Uses IndexedDB for structured data storage with good browser support.

Constructor​

new IndexedDBStorageProvider(dbName?: string)

Parameters​

  • dbName - Database name (default: 'VaultysPeerSDK')

Features​

  • Wide support: Available in all modern browsers
  • Large capacity: Typically 50% of available disk space
  • Structured storage: Optimized for JSON data
  • Indexed queries: Fast data retrieval

Example​

import { IndexedDBStorageProvider } from '@vaultys/peer-sdk';

const storage = new IndexedDBStorageProvider('MyApp');
await storage.initialize({ rootPath: 'peer-data' });

// Store data
await storage.write('contacts/alice.json', JSON.stringify({
did: 'did:vaultys:alice',
nickname: 'Alice',
addedAt: Date.now()
}));

// Read data
const data = await storage.read('contacts/alice.json');
const contact = JSON.parse(data.toString());

// List all contacts
const contacts = await storage.list('contacts');
for (const filename of contacts) {
const contactData = await storage.read(`contacts/${filename}`);
console.log('Contact:', JSON.parse(contactData.toString()));
}

// Clear all data
await storage.clear();

Additional Methods​

clear(): Promise<void>;
close(): Promise<void>;

Node.js Storage Providers​

NodeFSStorageProvider​

Uses the Node.js file system for storage.

Constructor​

new NodeFSStorageProvider(rootPath?: string)

Parameters​

  • rootPath - Root directory path (default: ~/.vaultys-peer-sdk)

Features​

  • Native file system: Direct file system access
  • Full capacity: Limited only by disk space
  • Synchronous option: Can use sync operations
  • File watching: Can monitor file changes

Example​

import { NodeFSStorageProvider } from '@vaultys/peer-sdk';

const storage = new NodeFSStorageProvider('./data');
await storage.initialize();

// Create nested directories
await storage.createDirectory('users/profiles');
await storage.createDirectory('users/settings');

// Write JSON data
const userData = {
id: '123',
name: 'John',
preferences: { theme: 'dark' }
};
await storage.write('users/profiles/123.json', JSON.stringify(userData));

// Read data
const data = await storage.read('users/profiles/123.json');
const user = JSON.parse(data.toString());

// Get file metadata
const metadata = await storage.getMetadata('users/profiles/123.json');
console.log('File size:', metadata.size);
console.log('Last modified:', metadata.modified);

// List directory contents
const profiles = await storage.list('users/profiles');
console.log('User profiles:', profiles);

// Check existence
const exists = await storage.exists('users/profiles/123.json');
console.log('File exists:', exists);

// Delete file
await storage.delete('users/profiles/123.json');

Memory Storage Provider​

Temporary storage using memory (RAM).

Constructor​

new MemoryStorageProvider()

Features​

  • Fastest performance: No I/O operations
  • No persistence: Data lost on reload
  • Good for testing: Ideal for unit tests
  • Cross-platform: Works everywhere

Example​

import { MemoryStorageProvider } from '@vaultys/peer-sdk';

const storage = new MemoryStorageProvider();
await storage.initialize();

// Use like any other provider
await storage.write('temp.txt', 'Temporary data');
const data = await storage.read('temp.txt');

// Data is lost when provider is destroyed

Creating Custom Providers​

You can create custom storage providers by implementing the StorageProvider interface:

import { StorageProvider, StorageMetadata } from '@vaultys/peer-sdk';

class CustomStorageProvider implements StorageProvider {
async initialize(config) {
// Initialize your storage backend
}

async read(filePath) {
// Read and return data as Buffer
return Buffer.from('data');
}

async write(filePath, data) {
// Write data to storage
}

async delete(filePath) {
// Delete file or directory
}

async exists(filePath) {
// Check if file exists
return true;
}

async list(dirPath) {
// List directory contents
return ['file1.txt', 'file2.txt'];
}

async createDirectory(dirPath) {
// Create directory
}

async getMetadata(filePath) {
// Return file metadata
return {
size: 100,
created: new Date(),
modified: new Date(),
isDirectory: false
};
}
}

// Use your custom provider
const storage = new CustomStorageProvider();
const peerService = new PeerService({
storageProvider: storage
});

Example: Redis Storage Provider​

import Redis from 'redis';

class RedisStorageProvider {
constructor(redisUrl) {
this.client = Redis.createClient({ url: redisUrl });
this.prefix = 'vaultys:';
}

async initialize(config) {
await this.client.connect();
if (config?.rootPath) {
this.prefix = `vaultys:${config.rootPath}:`;
}
}

async read(filePath) {
const key = this.prefix + filePath;
const data = await this.client.get(key);
return data ? Buffer.from(data, 'base64') : null;
}

async write(filePath, data) {
const key = this.prefix + filePath;
const value = Buffer.isBuffer(data)
? data.toString('base64')
: Buffer.from(data).toString('base64');

await this.client.set(key, value);

// Store metadata
const metaKey = `${key}:meta`;
await this.client.hSet(metaKey, {
size: value.length,
modified: Date.now(),
created: Date.now()
});
}

async delete(filePath) {
const key = this.prefix + filePath;
await this.client.del(key);
await this.client.del(`${key}:meta`);
}

async exists(filePath) {
const key = this.prefix + filePath;
return await this.client.exists(key) > 0;
}

async list(dirPath) {
const pattern = `${this.prefix}${dirPath}/*`;
const keys = await this.client.keys(pattern);

return keys
.filter(key => !key.endsWith(':meta'))
.map(key => key.replace(this.prefix, ''));
}

async createDirectory(dirPath) {
// Directories are virtual in Redis
const key = `${this.prefix}${dirPath}/.dir`;
await this.client.set(key, '1');
}

async getMetadata(filePath) {
const key = this.prefix + filePath;
const metaKey = `${key}:meta`;
const metadata = await this.client.hGetAll(metaKey);

if (!metadata || Object.keys(metadata).length === 0) {
return null;
}

return {
size: parseInt(metadata.size),
created: new Date(parseInt(metadata.created)),
modified: new Date(parseInt(metadata.modified)),
isDirectory: filePath.endsWith('/.dir')
};
}
}

Performance Comparison​

OperationOPFSIndexedDBLocalStorageNode FSMemory
Write 1KB1 ms3 ms2 ms1 ms< 1 ms
Write 1MB5 ms15 ms100 ms*3 ms< 1 ms
Read 1KB< 1 ms2 ms1 ms< 1 ms< 1 ms
Read 1MB2 ms8 ms50 ms*2 ms< 1 ms
List 10005 ms20 ms100 ms10 ms< 1 ms

*LocalStorage has 5-10MB limit and poor performance with large data

Best Practices​

  1. Choose the Right Provider:

    • Browser production: BrowserStorageProvider (auto-selects)
    • Node.js: NodeFSStorageProvider
    • Testing: MemoryStorageProvider
    • Modern browsers only: OPFSStorageProvider
  2. Handle Quota Errors:

    try {
    await storage.write('large-file.bin', largeData);
    } catch (error) {
    if (error.name === 'QuotaExceededError') {
    // Handle storage full
    await cleanupOldData();
    }
    }
  3. Request Persistence (Browser):

    if (storage.requestPersistence) {
    const granted = await storage.requestPersistence();
    if (!granted) {
    console.warn('Storage may be cleared by browser');
    }
    }
  4. Organize Data:

    // Use directory structure
    await storage.write('messages/2024/01/msg1.json', data);
    await storage.write('contacts/active/alice.json', data);
    await storage.write('settings/user.json', data);
  5. Clean Up:

    // Implement cleanup for old data
    async function cleanupOldMessages() {
    const messages = await storage.list('messages');
    const thirtyDaysAgo = Date.now() - (30 * 24 * 60 * 60 * 1000);

    for (const file of messages) {
    const metadata = await storage.getMetadata(`messages/${file}`);
    if (metadata && metadata.modified.getTime() < thirtyDaysAgo) {
    await storage.delete(`messages/${file}`);
    }
    }
    }
  6. Monitor Storage:

    async function checkStorageHealth() {
    if (storage.getStorageInfo) {
    const info = await storage.getStorageInfo();
    if (info.percentUsed > 80) {
    console.warn('Storage is getting full');
    await cleanupOldData();
    }
    }
    }