Ultra-small (<5KB) full-featured in-memory cache with TTL, LRU, stale-while-revalidate, async deduplication and zero dependencies
npm install nano-speed-cacheUltra-small (<5KB) in-memory cache for Node.js & browsers with TTL, LRU eviction, stale-while-revalidate, stale-if-error, async deduplication (getOrSet), disposal hooks, deep cloning, and zero dependencies.
Fast. Tiny. Fully-featured. Perfect for serverless, microservices, workers, or high-performance backend usage.
---
* โก Extremely fast O(1) get/set using Map
* ๐ง TTL support (per-key or default)
* โป๏ธ LRU eviction when max size is reached
* ๐ฐ๏ธ Stale-While-Revalidate (SWR) window support
* ๐ Stale-If-Error fallback
* ๐ซ Return stale values even after expiration (optional)
* ๐งต Async deduplication via getOrSet()
* ๐ Deep cloning via structuredClone (optional)
* ๐งน Background cleanup interval
* ๐๏ธ Custom disposeValue callback on deletion/eviction/expiration
* ๐ Stats: size, expired count, estimated memory usage
* ๐ฏ Zero dependencies
* ๐ฆ ESM + TypeScript typings included
---
``bash`
npm install nano-speed-cache
---
`js
import NanoSpeedCache from "nano-speed-cache";
const cache = new NanoSpeedCache({
defaultTTL: 60_000, // 1 min
maxSize: 5000,
staleWhileRevalidate: 30_000,
useClone: true
});
cache.set("user:123", { name: "Alice" }, 120_000);
console.log(cache.get("user:123"));
`
`js
const NanoSpeedCache = require("nano-speed-cache");
const cache = new NanoSpeedCache({ defaultTTL: 1000 });
cache.set("foo", "bar");
`
---
| Option | Type | Default | Description |
| ---------------------- | ---------------------- | ------- | --------------------------------------- |
| defaultTTL | number | 0 | Default TTL in ms (0 = never expires) |maxSize
| | number | 0 | Max entries (0 = unlimited) |checkPeriod
| | number | 10000 | Background cleanup interval |allowStale
| | boolean | false | Return stale expired values |staleWhileRevalidate
| | number | 0 | Serve stale value for N ms after expiry |staleIfError
| | number | 0 | Serve stale value if loader throws |useClone
| | boolean | false | Deep clone on get/set |disposeValue
| | (v,key,reason)=>void | null | Disposal callback |
---
Set value with optional TTL.
Get value; optionally disable touch for LRU.
Read without affecting LRU.
Delete a key.
Clear entire cache.
Get or update TTL.
Async deduplication:
`js`
const value = await cache.getOrSet("config", async () => {
return await fetchConfigFromDB();
});
If multiple requests come together, only one loader executes.
Returns a function that auto-caches:
`js`
const cachedFetch = cache.wrap("weather", fetchWeather, 60000);
await cachedFetch();
Returns:
`js`
{
size: number,
expired: number,
estimatedBytes: number
}
Supported events:
* "expire""evict"
*
---
LRU is based on:
``
score = lastTouchTimestamp * 1_000_000 - accessCount
This prevents high-frequency reads from being unfairly evicted.
---
`js
const cache = new NanoSpeedCache({
staleWhileRevalidate: 20_000
});
// returns stale value for 20 seconds after expiry
`
---
`js
const cache = new NanoSpeedCache({
staleIfError: 10_000
});
await cache.getOrSet("profile", async () => {
throw new Error("DB down");
});
// returns stale if available
`
---
Located in /demo/demo.js:
`js
const NanoSpeedCache = require("nano-speed-cache");
const cache = new NanoSpeedCache({
defaultTTL: 60_000,
maxSize: 5000,
staleWhileRevalidate: 30_000,
useClone: true
});
console.log(cache.get("user:123"));
`
---
`bash`
npm run build
`bash`
npm test
`json``
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "node",
"declaration": true,
"sourceMap": true,
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"outDir": "dist",
"rootDir": "src"
},
"include": ["src"]
}
---
MIT ยฉ 2025 Seyyed Ali Mohammadiyeh (Max Base)