mrkeyoor.com_
Sat 08 Aug 20:58 UTC
npmDataupdated 08 Aug 2026

cache-manager

cache-manager is an asynchronous Node.js cache facade built on Keyv stores. It gives applications one API for get, set, bulk operations, deletion, clearing, expiration lookup, function-result caching, refresh-ahead, events, and shutdown. One cache can layer memory before Redis, Postgres, MongoDB, SQLite, or other Keyv adapters, backfilling faster stores after a lower tier hits. Version 7 returns undefined on misses and includes an unbounded default in-memory store unless you configure a different Keyv backend.

Verdict

cache-manager 7 is a capable adapter-friendly choice for Node services that need wrap and layered caches. Configure bounds and persistence deliberately, treat nonBlocking as a best-effort mode, and budget real migration work if your code predates the Keyv-based version 6 API.

API stability3/5The current createCache surface is coherent and version 7 made only one advertised break from version 6, changing misses from null to undefined. The recent major history is still consequential: version 6 replaced legacy cache-manager stores with Keyv adapters, while version 7 changed a value that appears in nearly every get path. Some README signatures also lag the source, such as stores being an array rather than a function.
Docs3/5The package README is extensive, with migration notes, Keyv adapter setup, multi-store examples, every major method, refresh-ahead, events, serialization advice, and legacy adapter guidance. It is not internally consistent for 7.2.9: several get, mget, ttl, del, and clear examples still say null, stores is documented once like a method, and ttl wording obscures that the returned number is an absolute expiry timestamp.
Maintenance5/5Version 7.2.9 was published in June 2026, the monorepo was pushed in August 2026, and GitHub reports only 1 open issue or pull request in its combined counter. The package has active tests, TypeScript builds, dual module outputs, and current Keyv dependencies. Because statistics cover the whole cacheable monorepo, they also include sibling caching packages.
Ecosystem5/5cache-manager recorded 4,201,286 downloads for the measured week and the cacheable monorepo has 2,001 stars. The README names NestJS use, and the Keyv foundation opens Redis, Memcache, MongoDB, SQLite, Postgres, MySQL, etcd, lru-cache, and CacheableMemory options. Legacy adapters can be wrapped, though version 6 migration means not every older store is directly compatible.

Use it if

  • A Node service needs one cache API that can move from process memory to Keyv-backed Redis or another remote store
  • You need layered L1 and L2 caching with automatic reads from higher-priority stores and backfill after a lower-tier hit
  • Expensive async work needs wrap-based request coalescing and optional refresh-ahead within one process
  • Your framework or existing code already expects cache-manager semantics and version 7's undefined-on-miss behavior
Skip it if

Setup reality

npm install cache-manager gives you an ESM and CommonJS build, bundled TypeScript declarations, Keyv, and @cacheable/utils. createCache() works immediately with an in-memory Keyv instance whose serialization is disabled, but it is unbounded and per-process. Production persistence means choosing and installing a Keyv adapter separately, supplying its URL and credentials, and deciding whether values can be serialized safely. External Keyv instances use their own serializer settings; the README warns that Symbol values and types such as Uint8Array can come back incorrectly under JSON serialization unless you disable or replace serialization. TTL values are milliseconds. cache.ttl(key) returns the absolute expiration timestamp from Keyv, not milliseconds remaining, despite wording in parts of the README. Misses are undefined in v7, though several method examples lower in the README still show null. Multi-store order is priority order. Blocking mode checks stores sequentially, fills missing values from lower tiers for mget, and backfills earlier stores after wrap hits. nonBlocking changes the correctness contract by racing reads and returning before mutations settle, so use it only when best-effort caching is acceptable and watch adapter error events. refreshThreshold returns stale data and starts a background refresh; a slow worker can race key expiry, and no refresh occurs without a TTL. Remote adapters need explicit disconnect() during shutdown. Cache keys, invalidation, tenant separation, value versioning, and authorization boundaries remain application responsibilities.

Patterns

Create the default in-memory cachecreate-memory-cache

import { createCache } from 'cache-manager';

const cache = createCache({
  ttl: 60_000,
});

The default store is local to this process and has no configured size limit. Use a bounded memory adapter for untrusted or high-cardinality keys.

Set and read a typed valueset-and-get

type User = { id: string; name: string };

await cache.set<User>('user:42', { id: '42', name: 'Ada' }, 30_000);
const user = await cache.get<User>('user:42');

if (user === undefined) {
  console.log('cache miss');
}

TTL is milliseconds. Version 7 returns undefined, not null, for a missing or expired key.

Write and read several keysset-many-values

await cache.mset([
  { key: 'feature:a', value: true },
  { key: 'feature:b', value: false, ttl: 5_000 },
]);

const [a, b, missing] = await cache.mget<boolean>([
  'feature:a',
  'feature:b',
  'feature:none',
]);

mget preserves key order and uses undefined for misses. In blocking multi-store mode it fills missing positions from lower tiers.

Delete one key or flush the cachedelete-cache-entries

await cache.del('user:42');
await cache.mdel(['feature:a', 'feature:b']);

// Use sparingly: clears every configured store.
await cache.clear();

clear affects the entire configured cache namespace. Prefer versioned or tenant-scoped keys when broad invalidation is risky.

Cache and coalesce an expensive callwrap-expensive-function

const user = await cache.wrap(
  'user:42',
  () => database.users.findById('42'),
  60_000,
);

Concurrent wraps for the same cache ID and key coalesce inside this process only. Multiple service replicas can still run the worker simultaneously.

Choose TTL from the computed valueset-dynamic-ttl

const session = await cache.wrap('session:abc', loadSession, {
  ttl: (value) => value.isPremium ? 60_000 : 10_000,
});

Dynamic TTL is supported by wrap options. Return milliseconds and ensure every branch returns a valid positive duration for expiring entries.

Refresh a hot value in the backgroundrefresh-before-expiry

const cache = createCache({
  ttl: 60_000,
  refreshThreshold: 10_000,
});

const catalog = await cache.wrap('catalog:v3', loadCatalog);

A hit with less than the threshold remaining returns stale data immediately and refreshes in the background. Without a TTL, refresh does not trigger.

Return a value with its expiration timestampinspect-raw-expiry

const result = await cache.wrap('rates:usd', loadRates, {
  ttl: 30_000,
  raw: true,
});

console.log(result.value, new Date(result.expires));

expires is an absolute millisecond timestamp. Supply a TTL when requesting raw output so the expiration is meaningful.

Layer bounded memory in front of Redisconfigure-tiered-cache

import { CacheableMemory } from 'cacheable';
import { Keyv } from 'keyv';
import KeyvRedis from '@keyv/redis';

const memory = new Keyv({
  store: new CacheableMemory({ ttl: 60_000, lruSize: 5_000 }),
});
const redis = new Keyv({ store: new KeyvRedis(process.env.REDIS_URL) });

const cache = createCache({ stores: [memory, redis] });

Store order is priority order. Redis adds credentials, network failure modes, serialization choices, and a shutdown requirement.

Disable Keyv JSON serialization for binary valuescache-binary-values

import { Keyv } from 'keyv';

const memory = new Keyv();
memory.serialize = undefined;
memory.deserialize = undefined;

const binaryCache = createCache({ stores: [memory] });
await binaryCache.set('bytes', new Uint8Array([1, 2, 3]));

The default cache-manager memory store already disables serialization. Configure external Keyv instances explicitly when JSON would change the value.

Log cache errors and refreshesobserve-cache-events

cache.on('set', ({ key, error }) => {
  if (error) console.error('cache set failed', key, error);
});

cache.on('refresh', ({ key, error }) => {
  if (error) console.error('refresh failed', key, error);
});

Background refresh failures arrive through refresh events. Add listeners before traffic if cache failures must be observable.

Close remote adapters during shutdowndisconnect-cache-stores

async function shutdown() {
  await cache.disconnect();
  process.exitCode = 0;
}

process.once('SIGTERM', () => {
  void shutdown();
});

Remote adapters such as Redis may keep sockets open. Await disconnect only when the application is actually shutting down.

Alternatives

PackageRegistryPick it when
cacheablenpmYou want the same project's newer L1 and L2 caching framework with richer memory-cache controls
keyvnpmYou only need a small storage-agnostic key-value cache and prefer to build layering or wrapping yourself
lru-cachenpmA bounded in-process cache with explicit eviction behavior is enough and remote stores are unnecessary
@keyv/redisnpmYou want direct Keyv access to Redis without cache-manager's tiers, wrap, and event facade