Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

keyv-cache-proxy

Package Overview
Dependencies
Maintainers
1
Versions
12
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

keyv-cache-proxy

A transparent caching proxy for any object using Keyv - automatically cache method calls with TTL support

latest
Source
npmnpm
Version
0.2.3
Version published
Maintainers
1
Created
Source

keyv-cache-proxy

A transparent caching proxy for any object using Keyv - automatically cache method calls with TTL support.

Features

  • 🚀 Zero-config caching: Wrap any object to automatically cache all method calls
  • ⏱️ TTL support: Set time-to-live for cached values
  • 🔑 Flexible storage: Use any Keyv-compatible storage adapter
  • 🎯 Deep proxy: Automatically handles nested objects
  • 📊 Cache observability: Optional hooks for monitoring and modifying cached/fetched data
  • 🔄 Async-first: Automatically converts all methods to async

Installation

bun add keyv-cache-proxy keyv

Or with npm/yarn/pnpm:

npm install keyv-cache-proxy keyv

Quick Start

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');

Usage Examples

Basic Usage

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

With GitHub API (Octokit)

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

With Notion API

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',
});

With Slack API

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',
});

With Custom Storage Backends

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

Cache Observability & Data Modification

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

API

KeyvCacheProxy(options)

Creates a cache proxy factory function.

Options

  • store (required): A Keyv instance for cache storage
  • ttl (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).
    • Return undefined → Use original cached value
    • Return { skip: true } → Treat as cache miss and refetch
    • Return { data: <value> } → Return modified cached value
    • Signature: (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.
    • Return undefined → Cache original fetched value with default TTL
    • Return {} → Cache original fetched value with default TTL (same as undefined)
    • Return { data: <value> } → Cache modified value
    • Return { data: <value>, ttl: <ms> } → Cache modified value with custom TTL
    • Return { skip: true } → Skip caching but still return the fetched value
    • Signature: (key: string, value: any) => { data?: any, ttl?: number } | { skip: true } | undefined | Promise<...>

Returns

A function that takes an object and returns a proxied version with automatic caching.

Cache Key Generation

Cache keys are generated based on:

  • Method name
  • Arguments (JSON stringified)
  • Prefix (if provided)

Format: ${prefix}${methodName}:${JSON.stringify(args)}

Type Safety

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];
};

How It Works

The KeyvCacheProxy uses JavaScript Proxy to intercept method calls:

  • When a method is called, it generates a cache key from the method name and arguments
  • Checks the Keyv store for an existing cached result
  • If found (cache hit), returns the cached value
  • If not found (cache miss), executes the original method
  • Stores the result in the cache with the specified TTL
  • Returns the result

Nested objects are automatically wrapped with the same caching behavior.

Storage Backends

You can use any Keyv-compatible storage adapter:

See Also

Other Keyv storage adapters by the same author:

License

MIT © snomiao

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

  • keyv - Simple key-value storage with support for multiple backends
  • keyv-nedb-store - NeDB storage adapter for Keyv

Keywords

keyv

FAQs

Package last updated on 27 Feb 2026

Did you know?

Socket

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.

Install

Related posts