5-Minute Quickstart
Get up and running with Cachemate in under 5 minutes.
- TypeScript
- JavaScript (ESM / CJS)
src/cache.ts
import { Client, type TableRegistry } from 'cachemate';
// 1. Define table tags and version keys
const registry: TableRegistry = {
entries: {
users: { tag: 'u', versionKey: 'cm:ver:users' },
orders: { tag: 'o', versionKey: 'cm:ver:orders' },
products: { tag: 'p', versionKey: 'cm:ver:products' },
},
alias: {
userProfiles: 'users',
},
};
// 2. Initialize the Cachemate client
export const cachemate = new Client({
memoryMaxMb: 128, // Required in TS: In-memory fallback LRU size
redisUrls: [
process.env.REDIS_PRIMARY_URL ?? 'redis://localhost:6379',
process.env.REDIS_REPLICA_URL ?? 'redis://localhost:6380',
],
registry,
appName: 'ecommerce-api',
defaultTtl: 120, // 2 minutes
});
// 3. Cache a complex multi-table query
interface OrderSummary {
orderId: string;
total: number;
customerName: string;
}
export async function getOrderSummary(orderId: string): Promise<OrderSummary> {
return await cachemate.cache<OrderSummary>({
tables: ['orders', 'users'],
filters: { orderId },
ttlSeconds: 300,
loader: async () => {
// Executed ONLY on cache miss
console.log(`[DB] Fetching order summary from PostgreSQL for ${orderId}`);
return await db.query(
'SELECT o.id, o.total, u.name as customerName FROM orders o JOIN users u ON o.user_id = u.id WHERE o.id = $1',
[orderId]
);
},
});
}
// 4. Invalidate whenever a table changes
export async function updateOrder(orderId: string, data: any): Promise<void> {
await db.update('orders', orderId, data);
// Bumps version counter in Redis. All dependent order queries invalidate instantly!
await cachemate.invalidate('orders');
}
cache.js
// ESM: import { Client } from 'cachemate';
// CommonJS:
const { Client } = require('cachemate');
const client = new Client({
memoryMaxMb: 64,
redisUrls: [process.env.REDIS_URL || 'redis://localhost:6379'],
registry: {
entries: {
users: { tag: 'u', versionKey: 'ver:users' },
posts: { tag: 'p', versionKey: 'ver:posts' },
},
},
});
async function getUserFeed(userId) {
return await client.cache({
tables: ['users', 'posts'],
filters: { userId, page: 1 },
loader: async () => {
return await database.findPostsByUser(userId);
},
});
}
// Mutation:
async function createPost(userId, content) {
await database.insertPost({ userId, content });
await client.invalidate('posts');
}
How It Works Under the Hood
- Deterministic Key Generation: When you call
client.cache(), Cachemate reads the current versions of all requestedtables(ver:usersandver:posts) usingmget(). - Composite Key: It hashes your
filtersobject with SHA-1 and constructs a key:cm:p-u:1:4:a3f9e1b2c4d5. - Instant Invalidation: When you call
client.invalidate('posts'), the Redis counterver:postsincrements from4to5. - No Stale Data: The next lookup for
userIdautomatically asks forcm:p-u:1:5:a3f9e1b2c4d5. The old key...:1:4:...is simply ignored and cleaned up by Redis TTL automatically.