Skip to content

explita/cache

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

2 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

@explita/cache

License: MIT

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, and mdel.
  • πŸ› οΈ 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, deletes, and hit rate via cache.metrics.get().

πŸ“¦ Installation

pnpm add @explita/cache ioredis
# or
npm install @explita/cache ioredis

Redis 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_password

If 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
  l1Ttl: "1m", // 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,
    staleTtl: 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({ cleanupInterval: "10s" });
const redisStore = createRedisStore(myRedisClient);

βš™οΈ API Reference

CacheInstance Methods

Method Description
get(key, opts?) Get a value. Supports sliding expiration via { touch }.
pop(key) Get a value and delete it in one call.
set(key, value, opts?) Set a value with optional TTL, SWR, and tags.
setnx(key, value, opts?) Set only if key doesn't exist. Returns true/false.
cas(key, expected, new, opts?) Atomic compare-and-swap.
del(key) Delete a key.
mget(keys) Get multiple values in batch.
mset(entries, opts?) Set multiple values in batch.
mdel(keys) Delete multiple keys.
clear(pattern) Delete keys matching a glob pattern (e.g. "user:*").
has(key) Check if a key exists.
ttl(key) Get remaining TTL in seconds.
expire(key, ttl) Set TTL on an existing key.
remember(key, fn, opts?) Get or compute + cache. Supports SWR and stampede protection.
invalidate(tag) Delete all keys associated with a tag.
keys(pattern) List keys matching a glob pattern.
entries(pattern) Get key-value pairs matching a glob pattern.
namespace(prefix) Create a scoped sub-cache with an additional prefix.
metrics.get() Snapshot of hits, misses, hit rate, etc.
metrics.reset() Reset all metric counters.
destroy() Clean shutdown β€” clears intervals, disconnects Redis.

SetFnOpts

Option Type Default Description
ttl TTLWindow 300 (5m) Time-to-live in seconds or string like "1h".
staleTtl TTLWindow β€” SWR window β€” serve stale data while refreshing in background.
tags string[] [] Tags for bulk invalidation.

CacheConfig

Option Type Default Description
client Redis auto ioredis client. Auto-created from REDIS_HOST/REDIS_PORT env vars if omitted.
enableL1 boolean false Enable local memory cache (L1) on top of Redis (L2).
l1Ttl TTLWindow "1m" TTL for L1 entries (shorter than L2).
l1MaxKeys number β€” Max L1 keys before LRU eviction.
touch TTLWindow β€” Default sliding expiration β€” extends TTL on every get hit.
cleanupInterval TTLWindow 60 (1m) How often expired L1 entries are purged.
enableMemoryCleanup boolean true Enable periodic L1 cleanup interval.
logger CacheLogger console Logger for info/error messages.
loggingEnabled boolean true Toggle all logging.
serializer (v) => string JSON.stringify Custom serializer.
deserializer (s) => any JSON.parse Custom deserializer.

πŸ’– Support the Mission

@explita/cache is built to provide high-performance caching solutions with advanced features like SWR and stampede protection. If it has improved your application's speed or reliability, please consider supporting the project to ensure its continued growth and maintenance!

πŸš€ Ways to Contribute

  • Give us a ⭐: It helps others discover the project.
  • Join the Discussion: Report bugs or suggest new features.
  • Spread the Word: Share your experience with @explita/cache on social media.

πŸ™ Our Amazing Supporters

A huge thank you to everyone helping us build faster applications!

Contributors

πŸ“ License

MIT Β© Explita

About

Powerful hybrid caching library for Node.js with support for Memory and Redis, featuring stampede protection, namespacing, and metrics.

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Contributors