@explita/cache
v0.1.0
Published
Powerful hybrid caching library for Node.js with support for Memory and Redis, featuring stampede protection, namespacing, and metrics.
Downloads
71
Maintainers
Readme
@explita/cache
A high-performance, production-ready Hybrid L1/L2 Caching Suite for Node.js. Designed for speed, reliability, and developer experience.
✨ Features
- 🚀 Hybrid Architecture: Blazing fast Local Memory (L1) fallback to Distributed Redis (L2).
- 🔄 Stale-While-Revalidate (SWR): Serve stale data instantly while refreshing in the background.
- 🛡️ Stampede Protection: Prevents "Thundering Herd" by collapsing concurrent duplicate requests.
- 📡 Event-Driven: Built-in EventEmitter for monitoring hits, misses, expirations, and SWR triggers.
- 📦 Batch Operations: Atomic-like pipelines for
mget,mset, andmdel. - 🛠️ Custom Serialization: Plug-in support for
SuperJSON,msgpack, or any custom format. - 🧹 Pattern Clearing: Efficiently clear keys using glob patterns (e.g.,
user:*). - 🏷️ Tag-Based Invalidation: Group keys with tags and invalidate entire categories at once.
- 📊 Metrics: Built-in tracking for hits, misses, and set operations.
📦 Installation
pnpm add @explita/cache ioredis
# or
npm install @explita/cache ioredisRedis Configuration
To enable the Redis (L2) layer, simply install ioredis and set the following environment variables in your .env file:
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=optional_passwordIf these variables are present, the cache will automatically attempt to connect to Redis using a default internal client, unless a custom client is provided in the configuration.
🚀 Quick Start
import { createCache } from "@explita/cache";
const cache = createCache("app:", {
enableL1: true, // Enable local memory cache
l1TtlSeconds: 60, // L1 TTL (shorter than L2)
});
// Basic usage
await cache.set("users:1", { name: "Explita" }, { ttl: 3600 });
const user = await cache.get("users:1");
// Powerful 'remember' with SWR
const data = await cache.remember(
"profile:1",
async () => {
return await fetchProfileFromDB(1);
},
{
ttl: 60,
swr: 300, // Return stale data for 5 mins while revalidating
},
);🔌 Custom Serialization (Handling Dates, Maps, etc.)
Easily handle complex types using libraries like superjson:
import superjson from "superjson";
const cache = createCache("meta:", {
serializer: (v) => superjson.stringify(v),
deserializer: (v) => superjson.parse(v),
});
await cache.set("now", new Date()); // Works perfectly!🏷️ Tag-Based Invalidation
Group keys together and wipe them out in one go:
// Set data with tags
await cache.set("product:123", { name: "MacBook" }, { tags: ["products"] });
await cache.set("product:456", { name: "iPhone" }, { tags: ["products"] });
// Later, invalidate everything in the 'products' category
await cache.invalidate("products");📡 Monitoring with Events
cache.on("hit", (key, value) => console.log(`🔥 Hit: ${key}`));
cache.on("miss", (key) => console.log(`❄️ Miss: ${key}`));
cache.on("swr", (key) => console.log(`🔄 Refreshing: ${key}`));
cache.on("expired", (key) => console.log(`💀 Expired: ${key}`));🗃️ Storage Modules
You can also use the underlying stores standalone if you don't need the orchestrator:
import { createMemoryStore, createRedisStore } from "@explita/cache";
const mem = createMemoryStore({ cleanupIntervalMs: 10000 });
const redisStore = createRedisStore(myRedisClient);📝 License
MIT © Explita
