Simple key-value storage with support for multiple backends
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Configuration options and runtime property management for customizing serialization, compression, namespacing, and error handling behavior.
Complete configuration options for Keyv instances.
interface KeyvOptions {
/** Whether to emit error events (default: true) */
emitErrors?: boolean;
/** Namespace for key prefixing (default: 'keyv') */
namespace?: string;
/** Custom serialization function (default: JSON.stringify) */
serialize?: Serialize;
/** Custom deserialization function (default: JSON.parse) */
deserialize?: Deserialize;
/** Storage adapter instance (default: new Map()) */
store?: KeyvStoreAdapter | Map<any, any> | any;
/** Default TTL in milliseconds (default: undefined - no expiration) */
ttl?: number;
/** Compression adapter instance (default: undefined - no compression) */
compression?: CompressionAdapter | any;
/** Enable statistics tracking (default: false) */
stats?: boolean;
/** Enable key prefixing with namespace (default: true) */
useKeyPrefix?: boolean;
/** Throw errors in addition to emitting them (default: false) */
throwOnErrors?: boolean;
}
type Serialize = <Value>(data: DeserializedData<Value>) => Promise<string> | string;
type Deserialize = <Value>(data: string) => Promise<DeserializedData<Value> | undefined> | DeserializedData<Value> | undefined;Usage Examples:
import Keyv from "keyv";
import KeyvRedis from "@keyv/redis";
import KeyvGzip from "@keyv/compress-gzip";
// Complete configuration example
const keyv = new Keyv({
emitErrors: true,
namespace: 'my-app',
ttl: 3600000, // 1 hour default
store: new KeyvRedis('redis://localhost:6379'),
compression: new KeyvGzip(),
stats: true,
useKeyPrefix: true,
throwOnErrors: false,
serialize: JSON.stringify,
deserialize: JSON.parse
});
// Minimal configuration
const simple = new Keyv({
namespace: 'cache',
ttl: 300000 // 5 minutes
});Key prefixing and namespace management for isolation and collision avoidance.
class Keyv {
/** Get current namespace */
get namespace(): string | undefined;
/** Set namespace (updates storage adapter too) */
set namespace(namespace: string | undefined);
}
interface KeyvOptions {
/** Namespace for key prefixing */
namespace?: string;
/** Enable/disable key prefixing */
useKeyPrefix?: boolean;
}Usage Examples:
const keyv = new Keyv({ namespace: 'user-sessions' });
// Keys are automatically prefixed
await keyv.set('abc123', { userId: 456 });
// Actually stored as 'user-sessions:abc123'
// Change namespace at runtime
keyv.namespace = 'admin-sessions';
await keyv.set('xyz789', { userId: 789 });
// Stored as 'admin-sessions:xyz789'
// Disable key prefixing
keyv.useKeyPrefix = false;
await keyv.set('raw-key', 'value');
// Stored as 'raw-key' (no prefix)
// Remove namespace entirely
keyv.namespace = undefined;
console.log(keyv.namespace); // undefined
// Multiple instances with different namespaces
const userCache = new Keyv({ namespace: 'users' });
const productCache = new Keyv({ namespace: 'products' });
await userCache.set('123', { name: 'Alice' }); // users:123
await productCache.set('123', { name: 'Widget' }); // products:123
// No collision between user ID 123 and product ID 123Time-to-live settings for automatic expiration.
class Keyv {
/** Get current default TTL */
get ttl(): number | undefined;
/** Set default TTL for new keys */
set ttl(ttl: number | undefined);
}
interface KeyvOptions {
/** Default TTL in milliseconds */
ttl?: number;
}Usage Examples:
// Set default TTL at creation
const keyv = new Keyv({ ttl: 300000 }); // 5 minutes default
// All sets use default TTL unless overridden
await keyv.set('key1', 'value1'); // expires in 5 minutes
await keyv.set('key2', 'value2', 600000); // expires in 10 minutes (override)
await keyv.set('key3', 'value3', 0); // never expires (override)
// Change default TTL at runtime
keyv.ttl = 60000; // 1 minute
await keyv.set('key4', 'value4'); // expires in 1 minute
// Remove default TTL
keyv.ttl = undefined;
await keyv.set('key5', 'value5'); // never expires
// Check current TTL setting
console.log(keyv.ttl); // undefinedCustom serialization and deserialization functions.
class Keyv {
/** Get current serialization function */
get serialize(): Serialize | undefined;
/** Set serialization function */
set serialize(serialize: Serialize | undefined);
/** Get current deserialization function */
get deserialize(): Deserialize | undefined;
/** Set deserialization function */
set deserialize(deserialize: Deserialize | undefined);
}Usage Examples:
const keyv = new Keyv();
// Custom serialization for special data types
keyv.serialize = (data) => {
// Handle Date objects specially
if (data.value instanceof Date) {
return JSON.stringify({
...data,
value: { __type: 'Date', __value: data.value.toISOString() }
});
}
return JSON.stringify(data);
};
keyv.deserialize = (str) => {
const data = JSON.parse(str);
// Restore Date objects
if (data.value && data.value.__type === 'Date') {
data.value = new Date(data.value.__value);
}
return data;
};
// Now Date objects are preserved
await keyv.set('now', new Date());
const date = await keyv.get('now'); // Returns actual Date object
// Disable serialization entirely (for raw storage)
keyv.serialize = undefined;
keyv.deserialize = undefined;
// Now values are stored as-is (useful with generic stores)
// MessagePack serialization example
import msgpack from 'msgpack-lite';
keyv.serialize = (data) => msgpack.encode(data);
keyv.deserialize = (buffer) => msgpack.decode(buffer);Compression adapter integration for reducing storage space.
interface CompressionAdapter {
/** Compress value with optional options */
compress(value: any, options?: any): Promise<any>;
/** Decompress value with optional options */
decompress(value: any, options?: any): Promise<any>;
/** Serialize data with compression */
serialize<Value>(data: DeserializedData<Value>): Promise<string> | string;
/** Deserialize data with decompression */
deserialize<Value>(data: string): Promise<DeserializedData<Value> | undefined> | DeserializedData<Value> | undefined;
}
class Keyv {
/** Get current compression adapter */
get compression(): CompressionAdapter | undefined;
/** Set compression adapter */
set compression(compress: CompressionAdapter | undefined);
}Usage Examples:
import Keyv from "keyv";
import KeyvGzip from "@keyv/compress-gzip";
import KeyvBrotli from "@keyv/compress-brotli";
import KeyvLz4 from "@keyv/compress-lz4";
// Gzip compression
const keyvGzip = new Keyv({
compression: new KeyvGzip()
});
await keyvGzip.set('large-data', 'x'.repeat(10000)); // Compressed automatically
// Brotli compression (better compression ratio)
const keyvBrotli = new Keyv({
compression: new KeyvBrotli()
});
// LZ4 compression (faster compression/decompression)
const keyvLz4 = new Keyv({
compression: new KeyvLz4()
});
// Change compression at runtime
const keyv = new Keyv();
keyv.compression = new KeyvGzip();
await keyv.set('key1', 'large value'); // Compressed with gzip
keyv.compression = new KeyvBrotli();
await keyv.set('key2', 'another large value'); // Compressed with brotli
// Disable compression
keyv.compression = undefined;
await keyv.set('key3', 'uncompressed value'); // No compression
// Custom compression adapter
class CustomCompression {
async compress(value) {
// Your compression logic
return compressData(value);
}
async decompress(value) {
// Your decompression logic
return decompressData(value);
}
serialize(data) {
return JSON.stringify({
value: this.compress(data.value),
expires: data.expires
});
}
deserialize(str) {
const data = JSON.parse(str);
return {
value: this.decompress(data.value),
expires: data.expires
};
}
}
keyv.compression = new CustomCompression();Built-in operation statistics tracking.
class StatsManager {
/** Enable/disable statistics collection */
enabled: boolean;
/** Number of cache hits */
hits: number;
/** Number of cache misses */
misses: number;
/** Number of set operations */
sets: number;
/** Number of delete operations */
deletes: number;
/** Number of errors */
errors: number;
/** Record cache hit */
hit(): void;
/** Record cache miss */
miss(): void;
/** Record set operation */
set(): void;
/** Record delete operation */
delete(): void;
/** Record hits/misses from array */
hitsOrMisses<T>(array: Array<T | undefined>): void;
/** Reset all counters */
reset(): void;
}
class Keyv {
/** Access to statistics manager */
stats: StatsManager;
}Usage Examples:
const keyv = new Keyv({ stats: true });
// Perform operations
await keyv.set('key1', 'value1');
await keyv.set('key2', 'value2');
await keyv.get('key1'); // hit
await keyv.get('nonexistent'); // miss
await keyv.delete('key1');
// Check statistics
console.log(keyv.stats.hits); // 1
console.log(keyv.stats.misses); // 1
console.log(keyv.stats.sets); // 2
console.log(keyv.stats.deletes); // 1
// Reset statistics
keyv.stats.reset();
console.log(keyv.stats.hits); // 0
// Enable/disable at runtime
keyv.stats.enabled = false;
await keyv.get('key2'); // Won't count as hit/miss
keyv.stats.enabled = true;
// Custom statistics reporting
setInterval(() => {
const { hits, misses, sets, deletes } = keyv.stats;
const hitRate = hits / (hits + misses) * 100;
console.log(`Cache hit rate: ${hitRate.toFixed(2)}%`);
console.log(`Operations: ${sets} sets, ${deletes} deletes`);
}, 60000); // Every minuteControl error emission and throwing behavior.
class Keyv {
/** Get current throwOnErrors setting */
get throwOnErrors(): boolean;
/** Set throwOnErrors behavior */
set throwOnErrors(value: boolean);
}
interface KeyvOptions {
/** Emit error events (default: true) */
emitErrors?: boolean;
/** Throw errors in addition to emitting (default: false) */
throwOnErrors?: boolean;
}Usage Examples:
// Silent operation (no error events)
const silent = new Keyv({ emitErrors: false });
// Throwing operation (errors cause exceptions)
const throwing = new Keyv({ throwOnErrors: true });
try {
await throwing.set('key', 'value'); // May throw on storage error
} catch (error) {
console.error('Operation failed:', error);
}
// Both emit and throw
const verbose = new Keyv({
emitErrors: true,
throwOnErrors: true
});
verbose.on('error', (err) => console.log('Error event:', err));
try {
await verbose.get('key'); // May both emit event AND throw
} catch (error) {
console.error('Caught exception:', error);
}
// Change behavior at runtime
const keyv = new Keyv();
keyv.throwOnErrors = true; // Now operations will throwAccess to the complete options object.
class Keyv {
/** Access to current configuration options */
opts: KeyvOptions_;
}Usage Examples:
const keyv = new Keyv({
namespace: 'app',
ttl: 60000,
stats: true
});
// Access current configuration
console.log(keyv.opts.namespace); // 'app'
console.log(keyv.opts.ttl); // 60000
console.log(keyv.opts.stats); // true
// Configuration is read-only through opts
// Use property setters to modify:
keyv.namespace = 'new-namespace';
keyv.ttl = 120000;
console.log(keyv.opts.namespace); // 'new-namespace'
console.log(keyv.opts.ttl); // 120000