bridge-cache
v0.1.1
Published
In-memory cache bridge for frequently requested Node.js data with automatic invalidation
Downloads
190
Maintainers
Readme
bridge-cache
A production-ready in-memory cache for Node.js applications.
It is designed as a bridge for frequently requested data such as API responses, DB query results, computed objects, and JSON data.
Why this package
- Fast in-memory reads for hot data.
- Automatic invalidation by default (safe behavior if you forget options).
- Per-key invalidation options (
ttlMs,absoluteExpiryAt, tags). - Capacity controls with eviction strategies (
lru/fifo). - Stampede protection with
getOrSet(single-flight loading). - Small API surface, easy to adopt.
Install
npm install bridge-cacheQuick start
const { createCache } = require("bridge-cache");
const cache = createCache();
cache.set("app:config", { featureA: true });
const config = cache.get("app:config");
console.log(config);If you call set without options, default TTL invalidation applies automatically (default: 5 minutes).
Core concepts
setstores value with optional invalidation options.getreturns value on hit,undefinedon miss/expired.getOrSetfetches once for concurrent callers and stores result.invalidateTaginvalidates grouped keys.invalidateWhereinvalidates keys by custom predicate.statsgives counters and hit rate.
API
createCache(options?)
Creates an in-memory cache instance.
const cache = createCache({
defaultTtlMs: 5 * 60 * 1000,
maxEntries: 10_000,
maxSizeBytes: 64 * 1024 * 1024,
sweepIntervalMs: 60_000,
evictionPolicy: "lru",
cloneOnGet: false,
cloneOnSet: false,
onEvent: (event) => {
// Optional telemetry hook
console.log(event.type, event.key);
},
});Options:
defaultTtlMs(default300000): fallback expiry whensethas no ttl options.maxEntries(default10000): maximum number of entries.maxSizeBytes(default67108864): maximum total cache size.sweepIntervalMs(default60000): periodic cleanup interval for expired entries.evictionPolicy(default"lru"):"lru"or"fifo".cloneOnGet/cloneOnSet: optional defensive cloning.onEvent: callback for cache events (hit,miss,set,delete,expired,evicted,clear).
set(key, value, options?)
Stores a value.
cache.set("user:1", { id: 1, name: "Ana" }, { ttlMs: 60_000, tags: ["user"] });Set options:
ttlMs: relative expiry in milliseconds.absoluteExpiryAt: absolute Unix epoch timestamp in milliseconds.tags: label list for grouped invalidation.
Behavior:
- If
absoluteExpiryAtis provided, it is used. - Else if
ttlMsis provided, it is used. - Else
defaultTtlMsis applied automatically.
get(key) and has(key)
const value = cache.get("user:1");
if (cache.has("user:1")) {
// key exists and is not expired
}Expired entries are invalidated automatically on access.
delete(key) and clear()
cache.delete("user:1");
cache.clear();invalidateTag(tag)
cache.set("user:1", { id: 1 }, { tags: ["tenant:a", "user"] });
cache.set("user:2", { id: 2 }, { tags: ["tenant:a", "user"] });
const removed = cache.invalidateTag("tenant:a");
console.log(removed); // 2invalidateWhere(predicate)
cache.invalidateWhere((entry, key) => key.startsWith("product:") && entry.accessCount === 0);getOrSet(key, loader, options?)
Prevents duplicate concurrent loads for the same key.
const profile = await cache.getOrSet(
"user:1:profile",
async () => {
// Only one caller runs this loader concurrently per key
return fetchUserProfileFromDB(1);
},
{ ttlMs: 120_000, tags: ["user", "profile"] }
);stats()
console.log(cache.stats());
// {
// hits, misses, sets, deletes,
// evictions, expirations,
// count, sizeBytes, hitRate
// }stop()
Stops the internal sweeper interval (recommended in tests/short-lived workers).
cache.stop();End-to-end usage examples
1. Cache DB results by id
const { createCache } = require("bridge-cache");
const cache = createCache({ defaultTtlMs: 30_000 });
async function getUser(id) {
return cache.getOrSet(`user:${id}`, async () => {
return db.users.findById(id);
}, { tags: ["user"] });
}2. Cache external API response
const weatherCache = createCache({ defaultTtlMs: 60_000 });
async function getWeather(city) {
return weatherCache.getOrSet(`weather:${city}`, async () => {
const res = await fetch(`https://api.example.com/weather?city=${city}`);
return res.json();
}, { tags: ["weather"] });
}3. Invalidate related data after write
async function updateUser(userId, payload) {
await db.users.update(userId, payload);
cache.invalidateTag("user");
}Complete Example
Create a file named example.js:
const { createCache } = require('bridge-cache');
async function run() {
// 1. Initialize the cache
const cache = createCache({
defaultTtlMs: 60000, // 1 minute
maxEntries: 100,
evictionPolicy: 'lru'
});
// 2. Set some generic data
cache.set('user:123', { name: "Alice", active: true }, { tags: ['users'] });
console.log("User:", cache.get('user:123'));
// 3. getOrSet pattern (only executes loader if cache misses)
const apiData = await cache.getOrSet('weather:nyc', async () => {
console.log("Simulating slow API call...");
await new Promise(resolve => setTimeout(resolve, 500)); // Sleep
return { temp: 72, condition: "Sunny" };
}, { ttlMs: 15000 });
console.log("Weather:", apiData);
// 4. Invalidation
const removed = cache.invalidateTag('users');
console.log(`Removed ${removed} entries.`);
console.log("User after invalidation:", cache.get('user:123')); // undefined
// 5. Cleanup memory interval at process exit
cache.stop();
}
run();Data support
Supported well in v1:
- JSON-friendly objects
- arrays
- strings
- numbers
- booleans
null
Notes:
undefinedvalues are rejected.- Circular references are rejected (size estimation requires serialization).
Default invalidation strategy
If no invalidation options are passed in set, the package still invalidates automatically:
- Applies
defaultTtlMs(5 minutes by default). - Periodic sweep removes expired entries.
- Capacity checks evict entries when limits are exceeded.
This prevents accidental never-expiring cache entries.
Production recommendations
- Start with:
defaultTtlMs: 300000maxEntries: 10000maxSizeBytes: 64 * 1024 * 1024evictionPolicy: "lru"
- Add tags for domain-based invalidation (
user,catalog,tenant:x). - Export
stats()to logs/metrics for capacity tuning. - Use
getOrSetfor expensive loaders to prevent traffic spikes.
Development
npm install
npm run build
npm testRoadmap ideas
- Redis adapter.
- Stale-while-revalidate mode.
- Namespace support.
- Multi-process invalidation hooks.
Detailed design document
Implementation deep dive is available at:
