A library using the Vue Composition API for remote data fetching with stale-while-revalidate caching strategy
—
Configurable cache implementations for storing fetched data with TTL support, automatic expiration, and pluggable storage adapters.
Default in-memory cache implementation with automatic expiration and key serialization.
/**
* In-memory cache implementation with TTL support
* @param ttl - Default time to live in milliseconds (0 = no expiration)
*/
class SWRVCache<CacheData> {
constructor(ttl?: number);
/** Retrieve cached item by key */
get(k: string): ICacheItem<CacheData>;
/** Store item in cache with optional TTL override */
set(k: string, v: any, ttl: number): void;
/** Remove item from cache */
delete(serializedKey: string): void;
/** Convert complex keys to stable string identifiers */
serializeKey(key: IKey): string;
}Usage Examples:
import { SWRVCache } from "swrv";
// Basic cache usage
const cache = new SWRVCache();
cache.set('/api/users', userData, 0); // No expiration
const item = cache.get('/api/users');
// Cache with default TTL
const shortCache = new SWRVCache(5000); // 5 second default TTL
shortCache.set('/api/temp', tempData, 0); // Override with no expiration
shortCache.set('/api/quick', quickData, 1000); // Override with 1 second TTL
// Using with swrv configuration
const { data } = useSWRV('/api/data', fetcher, {
cache: new SWRVCache(300000) // 5 minute cache
});Persistent cache implementation using browser localStorage with JSON serialization.
/**
* localStorage-based cache adapter extending SWRVCache
* Data persists across browser sessions
* @param key - localStorage key prefix for all cache entries
* @param ttl - Default time to live in milliseconds
*/
class LocalStorageCache extends SWRVCache<any> {
constructor(key?: string, ttl?: number);
/** Retrieve cached item from localStorage */
get(k: string): ICacheItem<any>;
/** Store item in localStorage with TTL */
set(k: string, v: any, ttl: number): void;
/** Remove item from localStorage */
delete(serializedKey: string): void;
}Usage Examples:
import LocalStorageCache from "swrv/dist/cache/adapters/localStorage";
// Basic localStorage cache
const persistentCache = new LocalStorageCache();
// Custom storage key and TTL
const appCache = new LocalStorageCache('myapp-cache', 86400000); // 24 hour TTL
// Using with swrv for offline support
const { data } = useSWRV('/api/users', fetcher, {
cache: new LocalStorageCache('user-cache'),
shouldRetryOnError: false // Don't retry when offline
});
// Multiple cache instances for different data types
const userCache = new LocalStorageCache('users', 3600000); // 1 hour
const settingsCache = new LocalStorageCache('settings', 86400000); // 24 hours
const { data: users } = useSWRV('/api/users', fetcher, { cache: userCache });
const { data: settings } = useSWRV('/api/settings', fetcher, { cache: settingsCache });Structure of cached data with metadata for expiration and timestamps.
interface ICacheItem<Data> {
/** The actual cached data */
data: Data;
/** Timestamp when item was created */
createdAt: number;
/** Timestamp when item expires (Infinity for no expiration) */
expiresAt: number;
}Cache Item Examples:
// Accessing cache item metadata
const cache = new SWRVCache();
cache.set('/api/data', { users: [] }, 5000);
const item = cache.get('/api/data');
if (item) {
console.log('Data:', item.data);
console.log('Created:', new Date(item.createdAt));
console.log('Expires:', new Date(item.expiresAt));
console.log('Age:', Date.now() - item.createdAt);
}
// Check if item is expired
function isExpired(item) {
return Date.now() >= item.expiresAt;
}Convert complex cache keys to stable string identifiers for consistent caching.
/**
* Serialize complex keys to stable strings
* @param key - Key to serialize (string, array, function, or reactive)
* @returns Serialized string key
*/
serializeKey(key: IKey): string;Key Serialization Examples:
const cache = new SWRVCache();
// String keys (passed through)
cache.serializeKey('/api/users'); // '/api/users'
// Array keys (hashed to stable string)
cache.serializeKey(['/api/search', { q: 'vue', page: 1 }]); // 'arg@"/api/search"@0'
// Function keys (evaluated then serialized)
const userId = ref(123);
cache.serializeKey(() => `/api/users/${userId.value}`); // '/api/users/123'
// Null/undefined keys
cache.serializeKey(null); // ''
cache.serializeKey(undefined); // ''Create custom cache adapters by extending SWRVCache or implementing the same interface.
Custom Cache Examples:
// Redis cache adapter example
class RedisCache extends SWRVCache<any> {
constructor(private redisClient, ttl = 0) {
super(ttl);
}
async get(k: string) {
const key = this.serializeKey(k);
const data = await this.redisClient.get(key);
return data ? JSON.parse(data) : undefined;
}
async set(k: string, v: any, ttl: number) {
const key = this.serializeKey(k);
const timeToLive = ttl || this.ttl;
const item = {
data: v,
createdAt: Date.now(),
expiresAt: timeToLive ? Date.now() + timeToLive : Infinity
};
if (timeToLive) {
await this.redisClient.setex(key, Math.ceil(timeToLive / 1000), JSON.stringify(item));
} else {
await this.redisClient.set(key, JSON.stringify(item));
}
}
async delete(serializedKey: string) {
await this.redisClient.del(serializedKey);
}
}
// IndexedDB cache adapter example
class IndexedDBCache extends SWRVCache<any> {
constructor(private dbName = 'swrv-cache', ttl = 0) {
super(ttl);
}
async get(k: string) {
const key = this.serializeKey(k);
const db = await this.openDB();
const tx = db.transaction(['cache'], 'readonly');
const store = tx.objectStore('cache');
const result = await store.get(key);
if (result && Date.now() < result.expiresAt) {
return result;
}
return undefined;
}
async set(k: string, v: any, ttl: number) {
const key = this.serializeKey(k);
const timeToLive = ttl || this.ttl;
const item = {
key,
data: v,
createdAt: Date.now(),
expiresAt: timeToLive ? Date.now() + timeToLive : Infinity
};
const db = await this.openDB();
const tx = db.transaction(['cache'], 'readwrite');
const store = tx.objectStore('cache');
await store.put(item);
}
private async openDB() {
return new Promise((resolve, reject) => {
const request = indexedDB.open(this.dbName, 1);
request.onerror = () => reject(request.error);
request.onsuccess = () => resolve(request.result);
request.onupgradeneeded = () => {
const db = request.result;
if (!db.objectStoreNames.contains('cache')) {
db.createObjectStore('cache', { keyPath: 'key' });
}
};
});
}
}Common patterns for configuring caches in different scenarios.
Configuration Examples:
// Development vs Production caching
const isDev = process.env.NODE_ENV === 'development';
const cache = isDev
? new SWRVCache(1000) // Short TTL for dev
: new LocalStorageCache('prod-cache', 300000); // Persistent cache for prod
// Multi-tier caching strategy
const l1Cache = new SWRVCache(30000); // 30 second memory cache
const l2Cache = new LocalStorageCache('l2-cache', 3600000); // 1 hour persistent
class MultiTierCache extends SWRVCache<any> {
get(k: string) {
// Try L1 first
let item = l1Cache.get(k);
if (item) return item;
// Fall back to L2
item = l2Cache.get(k);
if (item) {
// Promote to L1
l1Cache.set(k, item.data, 30000);
return item;
}
return undefined;
}
set(k: string, v: any, ttl: number) {
l1Cache.set(k, v, Math.min(ttl, 30000)); // Max 30s in L1
l2Cache.set(k, v, ttl); // Full TTL in L2
}
}
// Cache per user/session
function createUserCache(userId: string) {
return new LocalStorageCache(`user-${userId}`, 3600000);
}
// Conditional caching based on data size
function createAdaptiveCache() {
return new (class extends SWRVCache<any> {
set(k: string, v: any, ttl: number) {
const size = JSON.stringify(v).length;
if (size > 100000) { // Large data to localStorage
const lsCache = new LocalStorageCache();
lsCache.set(k, v, ttl);
} else { // Small data to memory
super.set(k, v, ttl);
}
}
})();
}Install with Tessl CLI
npx tessl i tessl/npm-swrv