lru-cache, memory-cache, node-cache, and quick-lru are all in-memory caching solutions for Node.js, but they serve different architectural needs. lru-cache is a strict Least Recently Used (LRU) implementation ideal for limiting memory usage based on access patterns. node-cache focuses on Time-To-Live (TTL) expiration with event hooks, suitable for general purpose data caching. quick-lru is a minimal, fast LRU cache without dependencies, often used in simpler contexts or browsers. memory-cache is a legacy package often found in older tutorials but lacks active maintenance. Choosing the right one depends on whether you need strict LRU eviction, TTL-based expiration, or long-term stability.
When building Node.js applications, in-memory caching is a common strategy to reduce database load, speed up API responses, or store temporary computation results. The packages lru-cache, memory-cache, node-cache, and quick-lru all solve this problem, but they approach it differently. Some focus on strict Least Recently Used (LRU) eviction, while others prioritize Time-To-Live (TTL) expiration. Let's break down how they work and when to use each.
The most important difference is how these packages decide when to remove data.
lru-cache strictly follows the Least Recently Used algorithm.
// lru-cache: Strict LRU with optional TTL
import { LRUCache } from 'lru-cache';
const cache = new LRUCache({ max: 100, ttl: 1000 * 60 * 5 });
node-cache focuses on Time-To-Live (TTL).
maxKeys option, but eviction is not strictly LRU.// node-cache: TTL focused
const NodeCache = require('node-cache');
const cache = new NodeCache({ stdTTL: 300, checkperiod: 120 });
quick-lru is a simple count-based LRU.
// quick-lru: Simple count-based LRU
import QuickLRU from 'quick-lru';
const cache = new QuickLRU({ maxSize: 100 });
memory-cache uses TTL primarily.
node-cache but with fewer features.// memory-cache: TTL based (Legacy)
const MemoryCache = require('memory-cache');
const cache = new MemoryCache();
All four packages allow you to store and retrieve values, but the method names and units differ.
lru-cache uses set and get.
set or constructor.// lru-cache
import { LRUCache } from 'lru-cache';
const cache = new LRUCache({ max: 100 });
cache.set('key', 'value', { ttl: 5000 });
const value = cache.get('key');
node-cache uses set and get.
// node-cache
const NodeCache = require('node-cache');
const cache = new NodeCache();
cache.set('key', 'value', 100); // 100 seconds
const value = cache.get('key');
quick-lru uses set and get.
// quick-lru
import QuickLRU from 'quick-lru';
const cache = new QuickLRU({ maxSize: 100 });
cache.set('key', 'value');
const value = cache.get('key');
memory-cache uses put and get.
// memory-cache
const MemoryCache = require('memory-cache');
const cache = new MemoryCache();
cache.put('key', 'value', 5000); // 5000 milliseconds
const value = cache.get('key');
Expiration logic varies significantly, especially between milliseconds and seconds.
lru-cache handles TTL in milliseconds.
// lru-cache: TTL in ms
cache.set('session', data, { ttl: 60000 }); // 1 minute
node-cache handles TTL in seconds.
// node-cache: TTL in seconds
cache.set('session', data, 60); // 1 minute
quick-lru does not support TTL.
// quick-lru: No TTL support
cache.set('session', data); // Stays until maxSize reached
memory-cache handles TTL in milliseconds.
lru-cache but less configurable.// memory-cache: TTL in ms
cache.put('session', data, 60000); // 1 minute
This is a critical factor for production applications.
lru-cache is actively maintained.
// lru-cache: Modern ESM import
import { LRUCache } from 'lru-cache';
node-cache is actively maintained.
// node-cache: CommonJS require
const NodeCache = require('node-cache');
quick-lru is actively maintained.
// quick-lru: Modern ESM import
import QuickLRU from 'quick-lru';
memory-cache is unmaintained.
// memory-cache: Legacy warning
// β οΈ Not recommended for new development
const MemoryCache = require('memory-cache');
You need to cache API responses for 5 minutes to reduce external calls.
node-cache or lru-cache// node-cache example
if (!cache.has(url)) {
const data = await fetch(url);
cache.set(url, data, 300); // 5 minutes
}
You are caching file transformations in a build tool. Memory must not explode.
lru-cache or quick-lruquick-lru is lighter.// quick-lru example
const cache = new QuickLRU({ maxSize: 1000 });
cache.set(filePath, transformedContent);
Temporary user sessions that must expire automatically.
node-cache or lru-cache// lru-cache example
const cache = new LRUCache({ ttl: 1000 * 60 * 30 });
cache.set(userId, sessionData);
Maintaining an old Express app that already uses a specific cache.
memory-cache (Only if existing)// memory-cache example
// Only for legacy maintenance
cache.put('temp', value, 1000);
| Feature | lru-cache | node-cache | quick-lru | memory-cache |
|---|---|---|---|---|
| Strategy | Strict LRU + TTL | TTL + Max Keys | Strict LRU (Count) | TTL |
| TTL Unit | Milliseconds | Seconds | None | Milliseconds |
| Maintenance | β Active | β Active | β Active | β Unmaintained |
| Dependencies | None | None | None | None |
| Module Type | ESM (v9+) | CJS/ESM | ESM | CJS |
| Events | Limited | β Rich (del, expired) | None | None |
For most modern Node.js applications, lru-cache is the safest and most powerful choice. It balances strict memory management with TTL support and is actively maintained. Use node-cache if you prefer TTL in seconds and need event hooks for cache misses or expirations. quick-lru is excellent for lightweight tools or browser-based caching where you just need to limit item count. Avoid memory-cache in any new project due to its lack of maintenance and potential security risks.
Choose based on whether you need time-based expiration (node-cache, lru-cache) or strict memory limits (lru-cache, quick-lru), and always prioritize actively maintained packages.
Choose quick-lru if you need a lightweight, dependency-free LRU cache for simple use cases or browser environments. It is extremely fast and easy to set up but lacks TTL support and advanced features. Perfect for CLI tools, small utilities, or frontend build processes where you just need to limit the number of cached items by count.
Choose lru-cache if you need a robust, strictly enforced LRU algorithm with advanced features like async fetching and size calculation. It is the industry standard for high-performance caching where memory limits must be respected based on usage frequency. Ideal for build tools, complex server-side caching, and scenarios requiring precise control over eviction.
Avoid memory-cache for new projects as it is unmaintained and has not seen updates in years. It may still work for simple key-value storage in legacy systems, but it lacks modern features and security patches. Only use this if you are maintaining an older codebase that already depends on it and refactoring is not an option.
Choose node-cache if your primary requirement is Time-To-Live (TTL) expiration rather than strict LRU eviction. It offers useful events like del and expired, making it great for session storage or temporary data that must vanish after a set time. It is a solid choice for general-purpose caching where simplicity and TTL are more important than access-pattern optimization.
Useful when you need to cache something and limit memory usage.
See the algorithm section for implementation details.
npm install quick-lru
import QuickLRU from 'quick-lru';
const lru = new QuickLRU({maxSize: 1000});
lru.set('π¦', 'π');
lru.has('π¦');
//=> true
lru.get('π¦');
//=> 'π'
Returns a new instance.
It's a Map subclass.
Type: object
Required
Type: number
The target maximum number of items before evicting the least recently used items.
[!NOTE] This package uses an algorithm which maintains between
maxSizeand2 Γ maxSizeitems for performance reasons. The cache may temporarily contain up to twice the specified size due to the dual-cache design that avoids expensive delete operations.
Type: number
Default: Infinity
The maximum number of milliseconds an item should remain in the cache.
By default, maxAge will be Infinity, which means that items will never expire.
Lazy expiration occurs upon the next write or read call.
Individual expiration of an item can be specified by the set(key, value, options) method.
Optional
Type: (key, value) => void
Called right before an item is evicted from the cache due to LRU pressure, TTL expiration, or manual eviction via evict().
Useful for side effects or for items like object URLs that need explicit cleanup (revokeObjectURL).
[!NOTE] This callback is not called for manual removals via
delete()orclear(). It fires for automatic evictions and manual evictions viaevict().
The instance is an Iterable of [key, value] pairs so you can use it directly in a forβ¦of loop.
Both key and value can be of any type.
Set an item. Returns the instance.
Individual expiration of an item can be specified with the maxAge option. If not specified, the global maxAge value will be used in case it is specified in the constructor; otherwise, the item will never expire.
Get an item.
Check if an item exists.
Get an item without marking it as recently used.
Delete an item.
Returns true if the item is removed or false if the item doesn't exist.
Delete all items.
Get the remaining time to live (in milliseconds) for the given item, or undefined if the item is not in the cache.
Infinity if the item has no expiration (maxAge not set for the item and no global maxAge).Update the maxSize, discarding items as necessary. Insertion order is mostly preserved, though this is not a strong guarantee.
Useful for on-the-fly tuning of cache sizes in live systems.
Evict the least recently used items from the cache.
The count parameter specifies how many items to evict. Defaults to 1.
It will always keep at least one item in the cache.
import QuickLRU from 'quick-lru';
const lru = new QuickLRU({maxSize: 10});
lru.set('a', 1);
lru.set('b', 2);
lru.set('c', 3);
lru.evict(2); // Evicts 'a' and 'b'
console.log(lru.has('a'));
//=> false
console.log(lru.has('c'));
//=> true
Iterable for all the keys.
Iterable for all the values.
Iterable for all entries, starting with the oldest (ascending in recency).
Iterable for all entries, starting with the newest (descending in recency).
Iterable for all entries, starting with the oldest (ascending in recency).
This method exists for Map compatibility. Prefer .entriesAscending() instead.
Loop over entries calling the callbackFunction for each entry (ascending in recency).
This method exists for Map compatibility. Prefer .entriesAscending() instead.
The stored item count.
The set max size.
The set max age.
This library implements a variant of the hashlru algorithm using JavaScript's Map for broader key type support.
The algorithm uses a dual-cache approach with two Map objects:
On set() operations:
maxSize, promote it to become the old cache and create a fresh new cacheOn get() operations:
delete operations that can cause performance issues in JavaScript enginesmaxSize and 2 Γ maxSize items temporarilyChoose this implementation when:
Consider alternatives when:
maxSize items)