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:
- O(1) operations for get, set, delete, and has
- Optional TTL (Time-To-Live) support for automatic expiration
- Zero dependencies - pure JavaScript
- 100% test coverage - fully tested and reliable
- TypeScript support - full type definitions included
- ~2.2 KB minified and gzipped (compared to ~12 KB for lru-cache)
npm install tiny-lruRequires Node.js ≥14 or modern browsers with ES Module support.
import{lru}from"tiny-lru";// Create a cache that holds up to 100 itemsconstcache=lru(100);// Store and retrieve datacache.set("user:42",{name: "Alice",score: 1500});constuser=cache.get("user:42");// { name: "Alice", score: 1500 }// Chain operationscache.set("a",1).set("b",2).set("c",3);// Check what's in the cachecache.has("a");// truecache.size;// 3cache.keys();// ['a', 'b', 'c'] (LRU order)import{LRU}from"tiny-lru";interfaceUser{id: number;name: string;}constcache=newLRU<User>(100);cache.set("user:1",{id: 1,name: "Alice"});constuser: User|undefined=cache.get("user:1");Items can automatically expire after a set time:
// Cache that expires after 5 secondsconstsessionCache=lru(100,5000);sessionCache.set("session:id",{userId: 123});// After 5 seconds, this returns undefinedsessionCache.get("session:id");Want TTL to reset when you update an item? Enable resetTtl:
constcache=lru(100,60000,true);// 1 minute TTL, resets on updatecache.set("key","value");cache.set("key","new value");// TTL resetsGreat for:
- API response caching
- Function memoization
- Session storage with expiration
- Rate limiting
- LLM response caching
- Database query result caching
- Any scenario where you want to limit memory usage
Not ideal for:
- Non-string keys (works best with strings)
- Very large caches (consider a database)
Creates a new LRU cache instance with parameter validation.
import{lru}from"tiny-lru";constcache1=lru();// 1000 items, no TTLconstcache2=lru(500);// 500 items, no TTLconstcache3=lru(100,30000);// 100 items, 30s TTLconstcache4=lru(100,60000,true);// with resetTtl enabledParameters:
| 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
Creates an LRU cache instance without parameter validation.
import{LRU}from"tiny-lru";constcache=newLRU(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}. |
functionmemoize(fn,maxSize=100){constcache=lru(maxSize);returnfunction(...args){constkey=JSON.stringify(args);if(cache.has(key)){returncache.get(key);}constresult=fn(...args);cache.set(key,result);returnresult;};}// Cache expensive computationsconstfib=memoize((n)=>(n<=1 ? n : fib(n-1)+fib(n-2)),50);fib(100);// fast - cachedfib(100);// even faster - from cache// Cache instance shared across calls (outside the function)constcache=lru(1000,60000);// 1 minute cacheasyncfunctiongetUser(userId){// Check cache firstconstcached=cache.get(`user:${userId}`);if(cached){returncached;}// Fetch from databaseconstuser=awaitdb.users.findById(userId);// Store in cachecache.set(`user:${userId}`,user);returnuser;}constcache=lru(3);cache.set("a",1).set("b",2).set("c",3);constevicted=cache.setWithEvicted("d",4);console.log(evicted);// { key: 'a', value: 1, expiry: 0 }cache.keys();// ['b', 'c', 'd']constcache=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)constvalues=cache.values(["users:3","users:1"]);// ['Carol', 'Alice'] - matches input key orderimport_from"lodash";import{lru}from"tiny-lru";_.memoize.Cache=lru().constructor;constslowFunc=_.memoize(expensiveOperation);slowFunc.cache.max=100;// Configure cache sizeimport{LRU}from"tiny-lru";classAuthCache{constructor(){// Session cache: 30 minutes with TTL reset on updatethis.sessions=newLRU(10000,1800000,true);// Token validation cache: 5 minutes, no resetthis.tokens=newLRU(5000,300000,false);// Permission cache: 15 minutesthis.permissions=newLRU(5000,900000);}cacheSession(sessionId,userData,domain="app"){constkey=`${domain}:session:${sessionId}`;this.sessions.set(key,{userId: userData.userId,permissions: userData.permissions,loginTime: Date.now(),lastActivity: Date.now(),});}getSession(sessionId,domain="app"){constkey=`${domain}:session:${sessionId}`;returnthis.sessions.get(key);}}import{LRU}from"tiny-lru";classLLMCache{constructor(){// Cache up to 1000 responses for 1 hourthis.cache=newLRU(1000,3600000);// 1 hour TTL}asyncgetResponse(model,prompt,params={}){constkey=this.generateKey(model,prompt,params);// Check cache firstconstcached=this.cache.get(key);if(cached){return{ ...cached,fromCache: true};}// Make expensive API callconstresponse=awaitthis.callLLMAPI(model,prompt,params);// Cache the responsethis.cache.set(key,{response: response.text,tokens: response.tokens,timestamp: Date.now(),});return{ ...response,fromCache: false};}generateKey(model,prompt,params={}){constparamsHash=this.hashObject(params);constpromptHash=this.hashString(prompt);return`llm:${model}:${promptHash}:${paramsHash}`;}hashString(str){lethash=0;for(leti=0;i<str.length;i++){constchar=str.charCodeAt(i);hash=(hash<<5)-hash+char;hash=hash&hash;}returnMath.abs(hash).toString(36);}hashObject(obj){returnthis.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):
- Set: Add or update items
- Get: Retrieve and promote to most recent
- Delete: Remove items
- Has: Quick existence check
Run our comprehensive benchmark suite to see performance characteristics:
npm run benchmark:allSee 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 reportBuild 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 definitions
The 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% |
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Run
npm testto ensure all tests pass - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Implement a hierarchical key naming convention to prevent cross-domain data leakage:
{domain}:{service}:{resource}:{identifier}[:{version}]
Example domains:
- User-related:
usr:profile:data:12345 - Authentication:
auth:login:session:abc123 - External API:
api:response:endpoint:hash - Database:
db:query:sqlhash:paramshash - Application:
app:cache:feature:value - System:
sys:config:feature:version - Analytics:
analytics:event:user:session - ML/AI:
ml:llm:response:gpt4-hash
- API Reference - Complete API documentation
- Technical Documentation - Architecture, performance, and security
- Code Style Guide - Contributing guidelines
Copyright (c) 2026, Jason Mulligan
BSD-3-Clause