
Product
Socket for Jira Is Now Available
Socket for Jira lets teams turn alerts into Jira tickets with manual creation, automated ticketing rules, and two-way sync.
A fast, lightweight LRU (Least Recently Used) cache for JavaScript with O(1) operations and optional TTL support.
A high-performance, lightweight LRU (Least Recently Used) cache for JavaScript with O(1) operations and optional TTL support.
Think of an LRU cache like a limited-size bookshelf. When you add a new book and the shelf is full, you remove the least recently used book to make room. Every time you read a book, it moves to the front. This pattern is perfect for caching where you want to keep the most frequently accessed items.
The tiny-lru library provides:
npm install tiny-lru
Requires Node.js ≥14 or modern browsers with ES Module support.
import { lru } from "tiny-lru";
// Create a cache that holds up to 100 items
const cache = lru(100);
// Store and retrieve data
cache.set("user:42", { name: "Alice", score: 1500 });
const user = cache.get("user:42"); // { name: "Alice", score: 1500 }
// Chain operations
cache.set("a", 1).set("b", 2).set("c", 3);
// Check what's in the cache
cache.has("a"); // true
cache.size; // 3
cache.keys(); // ['a', 'b', 'c'] (LRU order)
import { LRU } from "tiny-lru";
interface User {
id: number;
name: string;
}
const cache = new LRU<User>(100);
cache.set("user:1", { id: 1, name: "Alice" });
const user: User | undefined = cache.get("user:1");
Items can automatically expire after a set time:
// Cache that expires after 5 seconds
const sessionCache = lru(100, 5000);
sessionCache.set("session:id", { userId: 123 });
// After 5 seconds, this returns undefined
sessionCache.get("session:id");
Want TTL to reset when you update an item? Enable resetTtl:
const cache = lru(100, 60000, true); // 1 minute TTL, resets on update
cache.set("key", "value");
cache.set("key", "new value"); // TTL resets
Great for:
Not ideal for:
lru(max?, ttl?, resetTtl?)Creates a new LRU cache instance with parameter validation.
import { lru } from "tiny-lru";
const cache1 = lru(); // 1000 items, no TTL
const cache2 = lru(500); // 500 items, no TTL
const cache3 = lru(100, 30000); // 100 items, 30s TTL
const cache4 = lru(100, 60000, true); // with resetTtl enabled
Parameters:
| Name | Type | Default | Description |
|---|---|---|---|
max | number | 1000 | Maximum items. 0 = unlimited. Must be >= 0. |
ttl | number | 0 | Time-to-live in milliseconds. 0 = no expiration. Must be >= 0. |
resetTTL | boolean | false | Reset TTL when updating existing items via set() |
Returns: LRU - New cache instance
Throws: TypeError if parameters are invalid
new LRU(max?, ttl?, resetTtl?)Creates an LRU cache instance without parameter validation.
import { LRU } from "tiny-lru";
const cache = new LRU(100, 5000);
Parameters:
| Name | Type | Default | Description |
|---|---|---|---|
max | number | 0 | Maximum items. 0 = unlimited. |
ttl | number | 0 | Time-to-live in milliseconds. 0 = no expiration. |
resetTTL | boolean | false | Reset TTL when updating via set() |
| Property | Type | Description |
|---|---|---|
first | object | null | Least recently used item (node with key, value, prev, next, expiry) |
last | object | null | Most recently used item (node with key, value, prev, next, expiry) |
max | number | Maximum items allowed |
resetTTL | boolean | Whether TTL resets on set() updates |
size | number | Current number of items |
ttl | number | Time-to-live in milliseconds |
| Method | Description |
|---|---|
cleanup() | Remove expired items without LRU update. Returns count of removed items. |
clear() | Remove all items. Returns this for chaining. |
delete(key) | Remove an item by key. Returns this for chaining. |
entries(keys?) | Get [key, value] pairs. Without keys: LRU order. With keys: input array order. |
evict() | Remove the least recently used item. Returns this for chaining. |
expiresAt(key) | Get expiration timestamp for a key. Returns `number |
forEach(callback, thisArg?) | Iterate over items in LRU order. Returns this for chaining. |
get(key) | Retrieve a value. Moves item to most recent. Returns value or undefined. |
getMany(keys) | Batch retrieve multiple items. Returns object mapping keys to values. |
has(key) | Check if key exists and is not expired. Returns boolean. |
hasAll(keys) | Check if ALL keys exist. Returns boolean. |
hasAny(keys) | Check if ANY key exists. Returns boolean. |
keys() | Get all keys in LRU order (oldest first). Returns string[]. |
keysByTTL() | Get keys by TTL status. Returns {valid, expired, noTTL}. |
onEvict(callback) | Register eviction callback (triggers on evict() or when set()/setWithEvicted() evicts). Returns this for chaining. |
peek(key) | Retrieve a value without LRU update. Returns value or undefined. |
set(key, value) | Store a value. Returns this for chaining. |
setWithEvicted(key, value) | Store value, return evicted item if full. Returns `{key, value, expiry} |
sizeByTTL() | Get counts by TTL status. Returns {valid, expired, noTTL}. |
stats() | Get cache statistics. Returns {hits, misses, sets, deletes, evictions}. |
toJSON() | Serialize cache to JSON format. Returns array of items. |
values(keys?) | Get all values, or values for specific keys. Returns array of values. |
valuesByTTL() | Get values by TTL status. Returns {valid, expired, noTTL}. |
function memoize(fn, maxSize = 100) {
const cache = lru(maxSize);
return function (...args) {
const key = JSON.stringify(args);
if (cache.has(key)) {
return cache.get(key);
}
const result = fn(...args);
cache.set(key, result);
return result;
};
}
// Cache expensive computations
const fib = memoize((n) => (n <= 1 ? n : fib(n - 1) + fib(n - 2)), 50);
fib(100); // fast - cached
fib(100); // even faster - from cache
// Cache instance shared across calls (outside the function)
const cache = lru(1000, 60000); // 1 minute cache
async function getUser(userId) {
// Check cache first
const cached = cache.get(`user:${userId}`);
if (cached) {
return cached;
}
// Fetch from database
const user = await db.users.findById(userId);
// Store in cache
cache.set(`user:${userId}`, user);
return user;
}
const cache = lru(3);
cache.set("a", 1).set("b", 2).set("c", 3);
const evicted = cache.setWithEvicted("d", 4);
console.log(evicted); // { key: 'a', value: 1, expiry: 0 }
cache.keys(); // ['b', 'c', 'd']
const cache = lru(100);
cache.set("users:1", { name: "Alice" });
cache.set("users:2", { name: "Bob" });
cache.set("users:3", { name: "Carol" });
// Get values for specific keys (order matches input array)
const values = cache.values(["users:3", "users:1"]);
// ['Carol', 'Alice'] - matches input key order
import _ from "lodash";
import { lru } from "tiny-lru";
_.memoize.Cache = lru().constructor;
const slowFunc = _.memoize(expensiveOperation);
slowFunc.cache.max = 100; // Configure cache size
import { LRU } from "tiny-lru";
class AuthCache {
constructor() {
// Session cache: 30 minutes with TTL reset on update
this.sessions = new LRU(10000, 1800000, true);
// Token validation cache: 5 minutes, no reset
this.tokens = new LRU(5000, 300000, false);
// Permission cache: 15 minutes
this.permissions = new LRU(5000, 900000);
}
cacheSession(sessionId, userData, domain = "app") {
const key = `${domain}:session:${sessionId}`;
this.sessions.set(key, {
userId: userData.userId,
permissions: userData.permissions,
loginTime: Date.now(),
lastActivity: Date.now(),
});
}
getSession(sessionId, domain = "app") {
const key = `${domain}:session:${sessionId}`;
return this.sessions.get(key);
}
}
import { LRU } from "tiny-lru";
class LLMCache {
constructor() {
// Cache up to 1000 responses for 1 hour
this.cache = new LRU(1000, 3600000); // 1 hour TTL
}
async getResponse(model, prompt, params = {}) {
const key = this.generateKey(model, prompt, params);
// Check cache first
const cached = this.cache.get(key);
if (cached) {
return { ...cached, fromCache: true };
}
// Make expensive API call
const response = await this.callLLMAPI(model, prompt, params);
// Cache the response
this.cache.set(key, {
response: response.text,
tokens: response.tokens,
timestamp: Date.now(),
});
return { ...response, fromCache: false };
}
generateKey(model, prompt, params = {}) {
const paramsHash = this.hashObject(params);
const promptHash = this.hashString(prompt);
return `llm:${model}:${promptHash}:${paramsHash}`;
}
hashString(str) {
let hash = 0;
for (let i = 0; i < str.length; i++) {
const char = str.charCodeAt(i);
hash = (hash << 5) - hash + char;
hash = hash & hash;
}
return Math.abs(hash).toString(36);
}
hashObject(obj) {
return this.hashString(JSON.stringify(obj, Object.keys(obj).sort()));
}
}
| Feature | tiny-lru | lru-cache | quick-lru |
|---|---|---|---|
| Bundle size | ~2.2 KB | ~12 KB | ~1.5 KB |
| O(1) operations | ✅ | ✅ | ✅ |
| TTL support | ✅ | ✅ | ✅ |
| TypeScript | ✅ | ✅ | ✅ |
| Zero dependencies | ✅ | ❌ | ✅ |
| Pure LRU | ✅ | ❌* | ✅ |
* lru-cache uses a hybrid design that can hold 2× the specified size for performance
All core operations are O(1):
Run our comprehensive benchmark suite to see performance characteristics:
npm run benchmark:all
See benchmarks/README.md for more details.
npm install # Install dependencies
npm test # Run lint and tests
npm run lint # Lint and check formatting
npm run fix # Fix lint and formatting issues
npm run build # Build distribution files
npm run coverage # Generate test coverage report
Build produces multiple module formats. When you install from npm, you'll get:
dist/tiny-lru.js - ES Modulesdist/tiny-lru.cjs - CommonJStypes/lru.d.ts - TypeScript definitionsThe minified version (dist/tiny-lru.min.js) is available in the repository for local testing but is not shipped via npm.
| Metric | Count |
|---|---|
| Tests | 149 |
| Suites | 26 |
| Metric | Coverage |
|---|---|
| Lines | 100% |
| Branches | 99.28% |
| Functions | 100% |
git checkout -b feature/amazing-feature)npm test to ensure all tests passgit commit -m 'Add amazing feature')git push origin feature/amazing-feature)Implement a hierarchical key naming convention to prevent cross-domain data leakage:
{domain}:{service}:{resource}:{identifier}[:{version}]
Example domains:
usr:profile:data:12345auth:login:session:abc123api:response:endpoint:hashdb:query:sqlhash:paramshashapp:cache:feature:valuesys:config:feature:versionanalytics:event:user:sessionml:llm:response:gpt4-hashCopyright (c) 2026, Jason Mulligan
BSD-3-Clause
The lru-cache package is another popular LRU cache implementation for Node.js. It offers more features and configuration options compared to tiny-lru, such as time-to-live (TTL) settings for cache entries and more detailed cache statistics.
The quick-lru package is a minimalist LRU cache implementation that focuses on performance and simplicity. It is similar to tiny-lru in terms of its lightweight nature but offers a slightly different API.
The node-cache package provides a simple and efficient in-memory caching solution with support for TTL and other advanced features. It is more feature-rich compared to tiny-lru, making it suitable for more complex caching needs.
FAQs
A fast, lightweight LRU (Least Recently Used) cache for JavaScript with O(1) operations and optional TTL support.
The npm package tiny-lru receives a total of 1,797,107 weekly downloads. As such, tiny-lru popularity was classified as popular.
We found that tiny-lru 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.

Product
Socket for Jira lets teams turn alerts into Jira tickets with manual creation, automated ticketing rules, and two-way sync.

Company News
Socket won two 2026 Reppy Awards from RepVue, ranking in the top 5% of all sales orgs. AE Alexandra Lister shares what it's like to grow a sales career here.

Security News
NIST will stop enriching most CVEs under a new risk-based model, narrowing the NVD's scope as vulnerability submissions continue to surge.