Skip to content

Repository files navigation

Tiny LRU

npm versionnpm downloadsLicenseNode.js versionBuild StatusCoverage

A high-performance, lightweight LRU (Least Recently Used) cache for JavaScript with O(1) operations and optional TTL support.

What is an LRU Cache?

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)

Installation

npm install tiny-lru

Requires Node.js ≥14 or modern browsers with ES Module support.

Quick Start

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)

TypeScript

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");

With TTL (Time-to-Live)

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 resets

When to Use Tiny LRU

Great 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)

API Reference

Factory Function: lru(max?, ttl?, resetTtl?)

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 enabled

Parameters:

NameTypeDefaultDescription
maxnumber1000Maximum items. 0 = unlimited. Must be >= 0.
ttlnumber0Time-to-live in milliseconds. 0 = no expiration. Must be >= 0.
resetTTLbooleanfalseReset TTL when updating existing items via set()

Returns:LRU - New cache instance

Throws:TypeError if parameters are invalid

Class: new LRU(max?, ttl?, resetTtl?)

Creates an LRU cache instance without parameter validation.

import{LRU}from"tiny-lru";constcache=newLRU(100,5000);

Parameters:

NameTypeDefaultDescription
maxnumber0Maximum items. 0 = unlimited.
ttlnumber0Time-to-live in milliseconds. 0 = no expiration.
resetTTLbooleanfalseReset TTL when updating via set()

Properties

PropertyTypeDescription
firstobject | nullLeast recently used item (node with key, value, prev, next, expiry)
lastobject | nullMost recently used item (node with key, value, prev, next, expiry)
maxnumberMaximum items allowed
resetTTLbooleanWhether TTL resets on set() updates
sizenumberCurrent number of items
ttlnumberTime-to-live in milliseconds

Methods

MethodDescription
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}.

Common Patterns

Memoization

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-Aside Pattern

// 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;}

Finding What Was Evicted

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']

Advanced Usage

Batch Operations with Keys

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 order

Interop with Lodash

import_from"lodash";import{lru}from"tiny-lru";_.memoize.Cache=lru().constructor;constslowFunc=_.memoize(expensiveOperation);slowFunc.cache.max=100;// Configure cache size

Session and Authentication Caching

import{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);}}

LLM Response Caching

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()));}}

Why Tiny LRU?

Featuretiny-lrulru-cachequick-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

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

Benchmarks

Run our comprehensive benchmark suite to see performance characteristics:

npm run benchmark:all

See benchmarks/README.md for more details.

Development

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 Output

Build produces multiple module formats. When you install from npm, you'll get:

  • dist/tiny-lru.js - ES Modules
  • dist/tiny-lru.cjs - CommonJS
  • types/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.

Tests

MetricCount
Tests149
Suites26

Test Coverage

MetricCoverage
Lines100%
Branches99.28%
Functions100%

Contributing

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Run npm test to ensure all tests pass
  4. Commit your changes (git commit -m 'Add amazing feature')
  5. Push to the branch (git push origin feature/amazing-feature)
  6. Open a Pull Request

Security

Multi-Domain Key Convention

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

Documentation

License

Copyright (c) 2026, Jason Mulligan

BSD-3-Clause

About

A fast, lightweight LRU (Least Recently Used) cache for JavaScript with O(1) operations and optional TTL support.

Resources

Contributing

Stars

184 stars

Watchers

1 watching

Forks

Releases

Sponsor this project

Used by

Contributors

Languages