
Research
/Security News
Mini Shai-Hulud Campaign Hits Red Hat Cloud Services npm Packages
A mini Shai-Hulud campaign compromised Red Hat Cloud Services npm packages to steal developer and CI/CD secrets during installation.
keyv-cache-proxy
Advanced tools
A transparent caching proxy for any object using Keyv - automatically cache method calls with TTL support
A transparent caching proxy for any object using Keyv - automatically cache method calls with TTL support.
bun add keyv-cache-proxy keyv
Or with npm/yarn/pnpm:
npm install keyv-cache-proxy keyv
import { KeyvCacheProxy } from 'keyv-cache-proxy';
import Keyv from 'keyv';
// Create a Keyv instance with any storage backend
const store = new Keyv();
// Wrap any object with caching
const cachedAPI = KeyvCacheProxy({
store,
})(yourAPIClient);
// All method calls are now automatically cached!
const result = await cachedAPI.fetchData('param1', 'param2');
import { KeyvCacheProxy } from 'keyv-cache-proxy';
import Keyv from 'keyv';
const myObject = {
expensiveOperation(a: number, b: number) {
console.log('Computing...');
return a + b;
}
};
const cached = KeyvCacheProxy({
store: new Keyv(),
ttl: 60000, // 1 minute
})(myObject);
// First call: executes the method
await cached.expensiveOperation(1, 2); // Logs: "Computing..."
// Second call: returns cached result
await cached.expensiveOperation(1, 2); // No log, returns from cache
import { Octokit } from 'octokit';
import { KeyvCacheProxy } from 'keyv-cache-proxy';
import Keyv from 'keyv';
import KeyvNedbStore from 'keyv-nedb-store';
// Use persistent storage
const kv = new Keyv(new KeyvNedbStore('.cache/github.yaml'));
const gh = KeyvCacheProxy({
store: kv,
ttl: 600000, // 10 minutes
prefix: 'github.',
onCached: (key, value) => console.log('Cache hit:', key),
onFetched: (key, value) => console.log('Fetched fresh:', key),
})(new Octokit().rest);
// API calls are now cached
const repo = await gh.repos.get({ owner: 'snomiao', repo: 'keyv-cache-proxy' });
import { Client } from '@notionhq/client';
import { KeyvCacheProxy } from 'keyv-cache-proxy';
import Keyv from 'keyv';
const notion = new Client({ auth: process.env.NOTION_API_KEY });
// Cache Notion API calls to reduce rate limiting
const cachedNotion = KeyvCacheProxy({
store: new Keyv(),
ttl: 300000, // 5 minutes
prefix: 'notion.',
})(notion);
// These calls will be cached
const database = await cachedNotion.databases.query({
database_id: 'your-database-id',
});
const page = await cachedNotion.pages.retrieve({
page_id: 'your-page-id',
});
import { WebClient } from '@slack/web-api';
import { KeyvCacheProxy } from 'keyv-cache-proxy';
import Keyv from 'keyv';
const slack = new WebClient(process.env.SLACK_TOKEN);
// Cache Slack API calls
const cachedSlack = KeyvCacheProxy({
store: new Keyv(),
ttl: 600000, // 10 minutes
prefix: 'slack.',
})(slack);
// Cached API calls
const channels = await cachedSlack.conversations.list();
const userInfo = await cachedSlack.users.info({ user: 'U123456' });
const messages = await cachedSlack.conversations.history({
channel: 'C123456',
});
import { KeyvCacheProxy } from 'keyv-cache-proxy';
import Keyv from 'keyv';
import KeyvRedis from '@keyv/redis';
import KeyvMongo from '@keyv/mongo';
// Redis
const redisCache = KeyvCacheProxy({
store: new Keyv(new KeyvRedis('redis://localhost:6379')),
ttl: 3600000,
})(yourObject);
// MongoDB
const mongoCache = KeyvCacheProxy({
store: new Keyv(new KeyvMongo('mongodb://localhost:27017')),
ttl: 3600000,
})(yourObject);
// SQLite (via keyv-sqlite)
const sqliteCache = KeyvCacheProxy({
store: new Keyv('sqlite://cache.db'),
ttl: 3600000,
})(yourObject);
Track cache performance:
let hits = 0;
let fetches = 0;
const cached = KeyvCacheProxy({
store: new Keyv(),
ttl: 60000,
onCached: (key, value) => {
hits++;
console.log(`Cache hit for ${key}. Total hits: ${hits}`);
},
onFetched: (key, value) => {
fetches++;
console.log(`Fetched fresh for ${key}. Total fetches: ${fetches}`);
},
})(myObject);
Modify cached/fetched data:
const cached = KeyvCacheProxy({
store: new Keyv(),
ttl: 60000,
// Add metadata to cached data (called on every invocation)
onCached: (key, value) => {
if (value !== undefined) {
console.log('Returning cached data');
return { data: { ...value, fromCache: true, cachedAt: Date.now() } };
}
},
// Transform fetched data before caching
onFetched: (key, value) => {
console.log('Processing fresh data');
return { data: { ...value, fetchedAt: Date.now(), processed: true } };
},
})(myObject);
Force cache refresh:
const cached = KeyvCacheProxy({
store: new Keyv(),
ttl: 60000,
onCached: (key, value) => {
// Return { skip: true } to force refetch even if cached
if (value && isStale(value)) {
return { skip: true }; // Forces cache miss and refetch
}
// Return undefined to use cached value
},
})(myObject);
Custom TTL per request:
const cached = KeyvCacheProxy({
store: new Keyv(),
ttl: 60000, // Default 1 minute
onFetched: (key, value) => {
// Cache user data longer than other data
if (key.includes('user')) {
return { data: value, ttl: 3600000 }; // 1 hour
}
return { data: value }; // Use default TTL
},
})(myObject);
KeyvCacheProxy(options)Creates a cache proxy factory function.
store (required): A Keyv instance for cache storagettl (optional): Time-to-live for cached entries in milliseconds (can be overridden per request via onFetched)prefix (optional): Prefix for cache keys (default: "")onCached (optional): Hook called on every invocation. Receives key and cached value (or undefined on cache miss).
undefined → Use original cached value{ skip: true } → Treat as cache miss and refetch{ data: <value> } → Return modified cached value(key: string, value: any) => { data?: any } | { skip: true } | undefined | Promise<...>onFetched (optional): Hook called when data is freshly fetched (cache miss). Receives key and fetched value.
undefined → Cache original fetched value with default TTL{} → Cache original fetched value with default TTL (same as undefined){ data: <value> } → Cache modified value{ data: <value>, ttl: <ms> } → Cache modified value with custom TTL{ skip: true } → Skip caching but still return the fetched value(key: string, value: any) => { data?: any, ttl?: number } | { skip: true } | undefined | Promise<...>A function that takes an object and returns a proxied version with automatic caching.
Cache keys are generated based on:
Format: ${prefix}${methodName}:${JSON.stringify(args)}
The proxy preserves TypeScript types and automatically converts all methods to async:
type DeepAsyncMethod<T> = {
[K in keyof T]: T[K] extends (...args: infer A) => infer R
? (...args: A) => Promise<Awaited<R>>
: T[K] extends object
? DeepAsyncMethod<T[K]>
: T[K];
};
The KeyvCacheProxy uses JavaScript Proxy to intercept method calls:
Nested objects are automatically wrapped with the same caching behavior.
You can use any Keyv-compatible storage adapter:
new Keyv()Other Keyv storage adapters by the same author:
MIT © snomiao
Contributions are welcome! Please feel free to submit a Pull Request.
FAQs
A transparent caching proxy for any object using Keyv - automatically cache method calls with TTL support
We found that keyv-cache-proxy demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Research
/Security News
A mini Shai-Hulud campaign compromised Red Hat Cloud Services npm packages to steal developer and CI/CD secrets during installation.

Research
/Security News
The North Korean malware loader hides in a Packagist-listed package and its GitHub branch to fetch and execute remote code in a likely Contagious Interview-style lure.

Security News
The Rust project is moving toward formal rules on LLM use in contributions after months of internal debate over maintainer burden, code quality, and contributor experience.