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