vsnode-cache.
The Classic TTL Cache vs a Modern TypeScript Rewrite
at a glance.
Compare node-cache and @humanspeak/memory-cache: two in-memory TTL caches for Node.js with different answers on LRU eviction, value cloning, TypeScript support, bulk invalidation, and method memoization.
side-by-side.
Every surface that matters, compared without spin.
| feature | @humanspeak/memory-cache | node-cache |
|---|---|---|
| Zero Dependencies node-cache depends on clone because it deep-clones values on set and get by default (useClones), which also adds per-operation overhead. | yes | no |
| TypeScript Support | Written in TypeScript | Bundled typings |
| TTL Expiration node-cache lets ttl(key, ttl) override the default per key. Memory Cache sets one TTL per cache instance — create purpose-scoped caches for different lifetimes. | Per cache instance | stdTTL + per-key override |
| LRU Eviction When node-cache reaches maxKeys, set() throws ECACHEFULL instead of evicting. Memory Cache evicts the least recently used key after pruning expired entries. | yes | no |
| Bounded Memory | maxSize with LRU eviction | maxKeys (set() throws when full) |
| Wildcard / Prefix Deletion | deleteByMagicString + deleteByPrefix | no |
| Method Memoization Decorator | @cached, async-aware | no |
| Async Fetch De-Duplication | getOrSet() collapses concurrent lookups | no |
| Lifecycle Instrumentation | Six lifecycle hooks | EventEmitter events (set, del, expired, flush) |
| Built-In Statistics Both track hits and misses. node-cache adds rough key/value size estimates; Memory Cache adds eviction and expiration counters. | yes | yes |
| Browser Support | yes | Node-focused |
| Active Maintenance | yes | Dormant (last release v5.1.2, 2020) |
where each shines.
- +Zero runtime dependencies — works in Node and the browser
- +TypeScript-first with fully typed generics (MemoryCache<T>)
- +TTL expiration and true LRU eviction in one cache — expired entries are pruned before any valid key is evicted
- +Wildcard + prefix bulk invalidation (deleteByMagicString, deleteByPrefix)
- +@cached decorator for method-level memoization — async-aware with in-flight de-duplication
- +getOrSet() async fetch helper that de-duplicates concurrent lookups for the same key
- +Lifecycle hooks (onHit, onMiss, onSet, onDelete, onExpire, onEvict) plus built-in getStats()
- +Synchronous reads and writes — no await on the hot path
- +Battle-tested with a huge install base and years of production use
- +Per-key TTL overrides (ttl, getTtl) and take() for read-and-delete
- +Value cloning by default isolates cached data from later mutations
- +Batch operations (mget, mset) out of the box
where each falls short.
- −Smaller community (newer project)
- −In-process only — no persistence or multi-process sharing by design
- −TTL is configured per cache instance, not per entry
- −No eviction policy — a full cache throws ECACHEFULL instead of making room
- −Deep-clones values on every set and get by default, a real cost for large objects
- −Dormant maintenance — no substantial release since 2020
- −No bulk invalidation, memoization decorator, or TypeScript-native API
the honest call.
node-cache is the incumbent for a reason: it is simple, stable, and everywhere, and its per-key TTL overrides are genuinely handy. But it has been dormant since 2020, clones every value by default, and throws when full instead of evicting. If you want a bounded cache that manages its own memory, typed generics, wildcard invalidation, and a @cached decorator, Memory Cache is the modern replacement. If you only need a tiny TTL map and already depend on node-cache, there is no urgent reason to switch.
read more.
Every head-to-head, with the same matrix + pros / cons + verdict format.
memory cache → install in 30 seconds