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
- Available Providers
- Common Interface
- Browser Storage Providers
- Node.js Storage Providers
- Memory Storage Provider
- Creating Custom Providers
- Performance Comparison
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โ
| Provider | Environment | Capacity | Performance | Persistence |
|---|---|---|---|---|
BrowserStorageProvider | Browser | Auto-detect | Auto-detect | Yes |
OPFSStorageProvider | Browser | GB+ | Excellent | Yes |
IndexedDBStorageProvider | Browser | ~50% disk | Good | Yes |
NodeFSStorageProvider | Node.js | Disk space | Excellent | Yes |
MemoryStorageProvider | Any | RAM | Excellent | No |
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โ
| Operation | OPFS | IndexedDB | LocalStorage | Node FS | Memory |
|---|---|---|---|---|---|
| Write 1KB | 1 ms | 3 ms | 2 ms | 1 ms | < 1 ms |
| Write 1MB | 5 ms | 15 ms | 100 ms* | 3 ms | < 1 ms |
| Read 1KB | < 1 ms | 2 ms | 1 ms | < 1 ms | < 1 ms |
| Read 1MB | 2 ms | 8 ms | 50 ms* | 2 ms | < 1 ms |
| List 1000 | 5 ms | 20 ms | 100 ms | 10 ms | < 1 ms |
*LocalStorage has 5-10MB limit and poor performance with large data
Best Practicesโ
-
Choose the Right Provider:
- Browser production:
BrowserStorageProvider(auto-selects) - Node.js:
NodeFSStorageProvider - Testing:
MemoryStorageProvider - Modern browsers only:
OPFSStorageProvider
- Browser production:
-
Handle Quota Errors:
try {
await storage.write('large-file.bin', largeData);
} catch (error) {
if (error.name === 'QuotaExceededError') {
// Handle storage full
await cleanupOldData();
}
} -
Request Persistence (Browser):
if (storage.requestPersistence) {
const granted = await storage.requestPersistence();
if (!granted) {
console.warn('Storage may be cleared by browser');
}
} -
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); -
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}`);
}
}
} -
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();
}
}
}