A high-performance, Bun-native caching library with namespace support, tag-based invalidation, batch operations, optional compression, and telemetry integration. Zero runtime dependencies β the Redis adapter runs on bun:redis, which ships with Bun itself.
- π Multiple adapters: Redis (via
bun:redis), Memory, and None (no-op) - π¦ Zero runtime dependencies β everything is built into Bun
- π§ TypeScript-first with complete type definitions
- π·οΈ Namespaces for logical isolation of cache entries
- π·οΈ Tag-based invalidation across namespaces
- π Batch operations:
mget,mset,deleteMany - π’ Atomic counters:
increment/decrementwith TTL seeding (Redis) - ποΈ Optional gzip compression with a configurable size threshold
- π Telemetry integration via
@nuvix/telemetry - π‘οΈ Safe by default: key/namespace validation, scoped flushes that never
issue
FLUSHALL, non-blockingSCAN+UNLINK
- Bun β₯ 1.2.21 (the Redis adapter uses the built-in
bun:redisclient)
bun add @nuvix/cache
# or
npm install @nuvix/cacheNote: v2 is ESM-only and requires the Bun runtime. For CommonJS or non-Bun environments, stay on v1.
import { Cache, Memory } from "@nuvix/cache";
const cache = new Cache(new Memory());
await cache.set(
"profile:123",
{ name: "John Doe" },
{
ttl: 1800, // seconds
tags: ["user", "profile"],
},
);
const profile = await cache.get<{ name: string }>("profile:123");
// Invalidate everything tagged "user"
await cache.flushByTags(["user"]);The Redis adapter accepts a connection URL, an options object, or an existing
RedisClient instance:
import { Cache, Redis } from "@nuvix/cache";
// 1. From a URL (also honors REDIS_URL / VALKEY_URL when constructed empty)
const redis = new Redis("redis://localhost:6379");
// 2. From options
const redis2 = new Redis({
url: "redis://localhost:6379",
namespace: "myapp",
defaultTTL: 3600,
keyPrefix: "nvx:cache:",
enableCompression: true, // gzip values above compressionThreshold
compressionThreshold: 1024,
});
// 3. Share an existing client β the adapter will NOT close it on close()
import { RedisClient } from "bun";
const shared = new RedisClient("redis://localhost:6379");
const redis3 = new Redis({ client: shared, namespace: "sessions" });
const cache = new Cache(redis);import { Cache, None } from "@nuvix/cache";
// Writes report success but store nothing β useful to disable caching in
// one environment without changing call sites.
const cache = new Cache(new None());cache.setDefaultNamespace("users");
await cache.set("k", "v"); // stored under "users"
await cache.set("k", "v", { namespace: "side" }); // stored under "side"
await cache.flushNamespace("side"); // clear one namespace
const keys = await cache.getKeysByNamespace("users", "k*");await cache.set("product:1", data, { tags: ["products", "featured"] });
await cache.set("product:2", data, { tags: ["products"] });
await cache.getKeysByTags(["products"]); // ["product:1", "product:2"]
await cache.flushByTags(["featured"]); // removes product:1 onlyawait cache.mset({ "user:1": alice, "user:2": bob }, { ttl: 3600 });
const users = await cache.mget(["user:1", "user:2", "missing"]);
await cache.deleteMany(["user:1"]);await cache.increment("page:views", 1); // seeds TTL on first write
await cache.decrement("quota:user", 1);await cache.exists("key");
await cache.expire("key", 300);
await cache.ttl("key"); // -2 missing, -1 no expiry
const stats = await cache.getStats(); // hits / misses / sets / deletes / errors / keyCountNeed the full adapter surface (patterns, namespace enumeration)? Keep a
reference to the adapter instance you constructed β every first-party
adapter implements EnhancedAdapter.
import { OpenTelemetry } from "@nuvix/telemetry";
cache.setTelemetry(new OpenTelemetry());
// Every operation records a duration sample into a histogram
// ("cache.operation.duration") tagged with operation and adapter.@nuvix/db consumes any driver exposing these four operations:
interface CacheDriver {
get<T>(key: string, options?: CacheOperationOptions): Promise<T | null>;
set<T>(
key: string,
value: T,
options?: CacheOperationOptions,
): Promise<boolean>;
flushByTags(tags: string[]): Promise<boolean>;
flush(): Promise<boolean>;
}
interface CacheOperationOptions {
ttl?: number; // seconds
tags?: string[];
}All first-party adapters satisfy this contract. Third-party drivers exposing
only these four methods also work through the facade; enhanced operations on
such drivers throw UnsupportedOperationError.
| Option | Default | Description |
|---|---|---|
url |
env / localhost | Connection URL (redis://user:pass@host:port/db) |
client |
β | Existing RedisClient (shared, never closed) |
namespace |
"default" |
Logical namespace segment |
defaultTTL |
3600 |
Default TTL in seconds |
keyPrefix |
"nvx:cache:" |
Prefix for every owned key |
enableCompression |
false |
Gzip values above the threshold |
compressionThreshold |
1024 |
Minimum serialized size in bytes |
maxKeyLength |
512 |
Maximum user key length |
Storage format: each value is a JSON envelope { d, c, e, m } (data,
created-at, expires-at, metadata) stored under
{keyPrefix}{namespace}:{key}. Tag membership lives in sets at
{keyPrefix}tags:{tag}.
import { Cache, Redis, Memory, None } from "@nuvix/cache";
import { Redis } from "@nuvix/cache/adapters/redis";
import { Memory } from "@nuvix/cache/adapters/memory";
import { None } from "@nuvix/cache/adapters/none";Types: CacheDriver, EnhancedAdapter, CacheOperationOptions,
CacheEntry, CacheStats, RedisAdapterInput, RedisAdapterOptions,
plus the error classes.
bun install # install dev dependencies
bun run lint # oxlint
bun run typecheck # tsc --noEmit
bun test # bun test runner
bun run build # bun build (ESM) + tsc declarations β dist/BSD 3-Clause β see the LICENSE file.
@nuvix/telemetryβ telemetry integration@nuvix/dbβ consumes this package'sCacheDrivercontract