Skip to main content

TypeScript Integration

Cachemate is written natively in TypeScript with end-to-end type safety, generic cache return types, and compile-time contract enforcement.


Strict Client Options

In TypeScript, ClientOptions requires memoryMaxMb to be explicitly declared. This guarantees that your application deliberately bounds its in-memory fallback footprint.

import { Client, type ClientOptions, type TableRegistry } from 'cachemate';

const options: ClientOptions = {
// Required in TS: In-memory LRU fallback cap in Megabytes
memoryMaxMb: 256,

// Optional: Array of Redis connection URLs or descriptor objects
redisUrls: [
'redis://10.0.1.1:6379',
{ url: 'redis://10.0.1.2:6379', maxMemoryMb: 512 },
],

// Optional: Table registry mapping logical/physical names to tags and version keys
registry: {
entries: {
users: { tag: 'usr', versionKey: 'cm:ver:users' },
accounts: { tag: 'acc', versionKey: 'cm:ver:accounts' },
},
alias: {
userAccounts: 'accounts',
},
},

// Optional: Application identifier prefix for logs
appName: 'payment-svc',

// Optional: Custom log handler callback
log: (msg: string) => console.debug(`[LOG] ${msg}`),

// Optional: Default TTL for join cache queries (seconds, default: 60)
defaultTtl: 180,

// Optional: Default TTL for flat namespace cache (seconds, default: 20)
flatTtl: 30,

// Optional: Global key prefix (default: 'cm')
keyPrefix: 'cm',
};

const client = new Client(options);

Generic Methods & Type Inference

Cachemate's core caching methods (cache, flat, rows) accept generics to type-check both the loader function and the resolved cache value.

1. cache<T>() — Multi-Table Join Cache

interface UserProfileWithRole {
id: string;
email: string;
role: 'admin' | 'member' | 'guest';
permissions: string[];
}

const profile = await client.cache<UserProfileWithRole>({
tables: ['users', 'roles'],
filters: { userId: 'usr_123', includePermissions: true },
ttlSeconds: 300,
loader: async (): Promise<UserProfileWithRole> => {
const user = await db.users.findUniqueOrThrow({ where: { id: 'usr_123' } });
const role = await db.roles.findUniqueOrThrow({ where: { id: user.roleId } });
return {
id: user.id,
email: user.email,
role: role.name as 'admin' | 'member' | 'guest',
permissions: role.permissions,
};
},
});

// profile is strongly typed as UserProfileWithRole
console.log(profile.role);

2. flat<T>() — Namespace Cache

For arbitrary entity or key-value caching independent of table version counters:

interface GeoLocationData {
city: string;
country: string;
coordinates: { lat: number; lng: number };
}

const geo = await client.flat<GeoLocationData>({
ns: 'ip-lookup',
filters: { ip: '198.51.100.42' },
ttlSeconds: 86400, // 24 hours
loader: async () => {
const res = await fetch('https://ip-api.example.com/198.51.100.42');
return (await res.json()) as GeoLocationData;
},
});

3. rows<T, M>() — Two-Tier Row Collection Cache

When caching paginated lists or search results, rows<T, M> separates the list query from individual row entities.

interface ProductRow {
id: string;
title: string;
price: number;
}

interface PaginationMeta {
page: number;
totalPages: number;
totalCount: number;
}

const result = await client.rows<ProductRow, PaginationMeta>({
namespace: 'products-search',
tables: ['products'],
filters: { category: 'electronics', page: 2 },
ttlSeconds: 600,
rowId: (product) => product.id,
getByAll: async () => {
const { items, meta } = await api.searchProducts({ category: 'electronics', page: 2 });
return { rows: items, meta };
},
getAllById: async (ids: string[]) => {
return await api.getProductsByIds(ids);
},
});

// result.rows is ProductRow[]
// result.meta is PaginationMeta | undefined
console.log(result.rows[0].price);
if (result.meta) {
console.log(`Page ${result.meta.page} of ${result.meta.totalPages}`);
}

Compiling and Module Resolution

Cachemate supports dual ESM and CJS modules. For best results in modern TypeScript projects:

tsconfig.json
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true
}
}