Custom Storage ​
Learn how to create custom storage adapters for @pubflow/flowfull-client.
TIP
@pubflow/flowfull-client automatically detects and uses localStorage (browser) or AsyncStorage (React Native). You can also provide a custom storage adapter for advanced use cases.
Storage Adapter Interface ​
All storage adapters must implement the StorageAdapter interface:
typescript
interface StorageAdapter {
getItem(key: string): Promise<string | null>;
setItem(key: string, value: string): Promise<void>;
removeItem(key: string): Promise<void>;
clear(): Promise<void>;
}Creating a Custom Adapter ​
Example: IndexedDB Adapter ​
For browsers, you might want to use IndexedDB instead of localStorage for larger storage capacity:
typescript
// src/storage/IndexedDBAdapter.ts
export class IndexedDBAdapter implements StorageAdapter {
private dbName: string;
private storeName: string;
private db: IDBDatabase | null = null;
constructor(dbName: string = 'pubflow', storeName: string = 'storage') {
this.dbName = dbName;
this.storeName = storeName;
}
private async getDB(): Promise<IDBDatabase> {
if (this.db) return this.db;
return new Promise((resolve, reject) => {
const request = indexedDB.open(this.dbName, 1);
request.onerror = () => reject(request.error);
request.onsuccess = () => {
this.db = request.result;
resolve(this.db);
};
request.onupgradeneeded = (event) => {
const db = (event.target as IDBOpenDBRequest).result;
if (!db.objectStoreNames.contains(this.storeName)) {
db.createObjectStore(this.storeName);
}
};
});
}
async getItem(key: string): Promise<string | null> {
const db = await this.getDB();
return new Promise((resolve, reject) => {
const transaction = db.transaction([this.storeName], 'readonly');
const store = transaction.objectStore(this.storeName);
const request = store.get(key);
request.onerror = () => reject(request.error);
request.onsuccess = () => resolve(request.result || null);
});
}
async setItem(key: string, value: string): Promise<void> {
const db = await this.getDB();
return new Promise((resolve, reject) => {
const transaction = db.transaction([this.storeName], 'readwrite');
const store = transaction.objectStore(this.storeName);
const request = store.put(value, key);
request.onerror = () => reject(request.error);
request.onsuccess = () => resolve();
});
}
async removeItem(key: string): Promise<void> {
const db = await this.getDB();
return new Promise((resolve, reject) => {
const transaction = db.transaction([this.storeName], 'readwrite');
const store = transaction.objectStore(this.storeName);
const request = store.delete(key);
request.onerror = () => reject(request.error);
request.onsuccess = () => resolve();
});
}
async clear(): Promise<void> {
const db = await this.getDB();
return new Promise((resolve, reject) => {
const transaction = db.transaction([this.storeName], 'readwrite');
const store = transaction.objectStore(this.storeName);
const request = store.clear();
request.onerror = () => reject(request.error);
request.onsuccess = () => resolve();
});
}
}Example: Redis Adapter (Node.js/Bun) ​
For server-side applications, you might want to use Redis:
typescript
// src/storage/RedisAdapter.ts
import { StorageAdapter } from '@pubflow/flowfull-client';
import Redis from 'ioredis';
export class RedisAdapter implements StorageAdapter {
private client: Redis;
private prefix: string;
constructor(redisUrl: string, prefix: string = 'flowfull:') {
this.client = new Redis(redisUrl);
this.prefix = prefix;
}
private getKey(key: string): string {
return `${this.prefix}${key}`;
}
async getItem(key: string): Promise<string | null> {
return await this.client.get(this.getKey(key));
}
async setItem(key: string, value: string): Promise<void> {
await this.client.set(this.getKey(key), value);
}
async removeItem(key: string): Promise<void> {
await this.client.del(this.getKey(key));
}
async clear(): Promise<void> {
const keys = await this.client.keys(`${this.prefix}*`);
if (keys.length > 0) {
await this.client.del(...keys);
}
}
async disconnect(): Promise<void> {
await this.client.quit();
}
}Using Custom Storage ​
Browser (React/Vue/etc.) ​
typescript
// src/api/client.ts
import { createFlowfull } from '@pubflow/flowfull-client';
import { IndexedDBAdapter } from './storage/IndexedDBAdapter';
const customStorage = new IndexedDBAdapter();
export const api = createFlowfull(import.meta.env.VITE_API_URL, {
storage: customStorage
});React Native/Expo ​
typescript
// src/api/client.ts
import { createFlowfull } from '@pubflow/flowfull-client';
import * as SecureStore from 'expo-secure-store';
// Create a secure storage adapter
class SecureStorageAdapter {
async getItem(key: string): Promise<string | null> {
return await SecureStore.getItemAsync(key);
}
async setItem(key: string, value: string): Promise<void> {
await SecureStore.setItemAsync(key, value);
}
async removeItem(key: string): Promise<void> {
await SecureStore.deleteItemAsync(key);
}
async clear(): Promise<void> {
// SecureStore doesn't have a clear method
// You'll need to track keys manually
}
}
const secureStorage = new SecureStorageAdapter();
export const api = createFlowfull(process.env.EXPO_PUBLIC_API_URL!, {
storage: secureStorage
});Node.js/Bun Server ​
typescript
// src/lib/api.ts
import { createFlowfull } from '@pubflow/flowfull-client';
import { RedisAdapter } from './storage/RedisAdapter';
const redisStorage = new RedisAdapter(process.env.REDIS_URL!);
export function createServerApi(sessionId?: string) {
return createFlowfull(process.env.API_URL!, {
sessionId,
storage: redisStorage
});
}Storage Keys ​
@pubflow/flowfull-client uses the following storage keys:
pubflow_session_id- Session ID for authenticationpubflow_user_data- Cached user data (optional)
TIP
The client automatically prefixes keys with pubflow_ to avoid conflicts with other data in storage.
Best Practices ​
- Implement Error Handling: Always handle storage errors gracefully
- Use Prefixes: Prefix your keys to avoid conflicts with other data
- Consider TTL: Implement time-to-live for cached data if needed
- Test Thoroughly: Test your adapter with different scenarios
- Handle Quota Exceeded: Implement fallback for storage quota errors
- Secure Sensitive Data: Use secure storage for sensitive information
Next Steps ​
- Multi-Instance - Multiple API instances
- Performance - Optimization tips
- API Reference - Full API documentation