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();
    }
    }
    }