This guide walks you through installing Ziggurat and creating your first cache.
Ziggurat is distributed as separate packages so you only install what you need.
npm install @ziggurat-cache/coreThe core package includes the CacheManager orchestrator and the built-in MemoryAdapter.
npm install @ziggurat-cache/redis ioredisioredis is a peer dependency — you provide and manage your own Redis client.
npm install @ziggurat-cache/nestjsRequires @nestjs/common and @nestjs/core >= 10.x as peer dependencies.
Every Ziggurat setup starts with a CacheManager and at least one adapter (layer). Each adapter can define its own defaultTtlMs, and the manager can set a namespace to prefix all keys:
import { CacheManager, MemoryAdapter } from "@ziggurat-cache/core";
const cache = new CacheManager({
namespace: "users",
layers: [new MemoryAdapter({ defaultTtlMs: 300_000 })],
});The wrap method is the primary API. It checks the cache first. On a miss, it calls your factory function, stores the result, and returns it. TTL is handled by the adapter's defaultTtlMs:
async function getUserProfile(userId: string) {
return cache.wrap(
`profile:${userId}`, // stored as "users:profile:42"
async () => {
// This only runs on cache miss
return await db.users.findById(userId);
},
);
}On the first call, the factory executes and the result is cached. On subsequent calls within the TTL window, the cached value is returned instantly — no database query.
You can also interact with the cache directly:
// Store a value
await cache.set("config:feature-flags", flags, 60_000);
// Retrieve a value
const entry = await cache.get<FeatureFlags>("config:feature-flags");
if (entry) {
console.log(entry.value); // typed as FeatureFlags
console.log(entry.expiresAt); // Unix timestamp or null
}
// Delete a specific key
await cache.delete("config:feature-flags");
// Clear all cached data
await cache.clear();To add distributed caching, stack a Redis adapter behind the memory layer. Each adapter defines its own TTL — memory expires fast, Redis holds data longer:
import { CacheManager, MemoryAdapter } from "@ziggurat-cache/core";
import { RedisAdapter } from "@ziggurat-cache/redis";
import Redis from "ioredis";
const cache = new CacheManager({
namespace: "products",
layers: [
new MemoryAdapter({ maxTtlMs: 30_000 }), // L1: capped at 30s
new RedisAdapter({ client: new Redis(), defaultTtlMs: 600_000 }), // L2: 10min
],
});Reads check L1 first. On an L1 miss, L2 is checked. If L2 has the value, it's returned and L1 is automatically backfilled with the entry's remaining TTL, capped at L1's maxTtlMs, so the next read is served from memory.
- Core Concepts — Understand layers, backfill, and stampede protection
- API Reference — Full API documentation for all packages
- Redis Adapter — Redis-specific configuration and tips
- NestJS Integration — Module setup and
@Cached()decorator