Overview
The cache system provides a flexible way to cache handler responses and intermediate computations with support for custom cache stores and key resolution strategies.
createCache
Creates a cache function for use within handlers.
import { createCache } from '@apisr/controller';
const cache = createCache({
payload: { userId: '123' },
handlerCache: {
store: myStore,
key: ['user', 'profile'],
},
callCache: {
wrapHandler: true,
},
});
const result = await cache(['expensive', 'operation'], async () => {
return await performExpensiveOperation();
});
opts
CreateCacheOptions
required
Options for creating the cache functionThe handler payload used for cache key resolution
Default cache options from handler configuration
Override cache options from handler call
A cache function for memoizing operations
CacheFn
The cache function signature returned by createCache.
type CacheFn = <TValue = unknown>(
key: CacheKey,
callback: CacheCallback<TValue>,
options?: CacheCallbackOptions
) => Promise<TValue>;
Cache key as a string or array of strings// String key
await cache('user-profile', async () => {...});
// Array key (joined with ':')
await cache(['user', userId, 'profile'], async () => {...});
callback
CacheCallback<TValue>
required
Function to execute if cache miss occurstype CacheCallback<TValue> = () => Promise<TValue> | TValue;
Additional options for this cache operationTime-to-live in milliseconds
Additional properties are passed through to the store implementation
The cached or computed value
CacheOptions
Configuration options for cache behavior.
interface CacheOptions<TPayload, TStore extends CacheStore> {
key?: CacheKeyInput<TPayload>;
store: TStore;
wrapHandler?: boolean;
}
Base cache key or resolver function// Static key
key: ['api', 'users']
// Dynamic key resolver
key: ({ payload }) => ['user', payload.userId]
Cache store implementation
If true, caches the entire handler response instead of individual operations
CacheStore
Interface for implementing custom cache stores.
interface CacheStore {
get<TValue = unknown>(key: string): Promise<TValue | undefined> | TValue | undefined;
set<TValue = unknown>(
key: string,
value: TValue,
options?: CacheCallbackOptions
): Promise<void> | void;
delete?(key: string): Promise<boolean | void> | boolean | void;
}
get
(key: string) => Promise<TValue | undefined>
required
Retrieve a value from the cacheThe cached value, or undefined if not found
set
(key: string, value: TValue, options?) => Promise<void>
required
Store a value in the cacheAdditional options (e.g., TTL)
delete
(key: string) => Promise<boolean | void>
Optional method to delete a cached valueWhether the key was deleted
CacheKeyResolver
Function type for dynamically resolving cache keys.
type CacheKeyResolver<TPayload = unknown> = (
data: CacheKeyResolverData<TPayload>
) => CacheKey;
data
CacheKeyResolverData
required
The resolved cache key (string or string array)
Utility Functions
resolveCacheOptions
Merges handler and call cache options.
import { resolveCacheOptions } from '@apisr/controller';
const resolved = resolveCacheOptions({
handlerCache: { store: myStore, key: ['base'] },
callCache: { wrapHandler: true },
});
Handler-level cache options
Call-level cache options (overrides handler options)
Merged cache options, or undefined if no store is configured
resolveCacheKey
Resolves a cache key from inputs.
import { resolveCacheKey } from '@apisr/controller';
const key = resolveCacheKey({
key: ['user', 'profile'],
payload: { userId: '123' },
baseKey: ['api'],
});
// Result: 'api:user:profile'
Whether wrapping the entire handler
The resolved cache key string (parts joined with :)
Usage Examples
Handler-Level Caching
import { createHandler } from '@apisr/controller';
import Keyv from 'keyv';
const store = new Keyv();
const handler = createHandler({
cache: {
store,
wrapHandler: true,
key: ({ payload }) => ['user', payload.userId],
},
});
const getUser = handler(
async ({ payload }) => {
// This entire handler will be cached
return await db.users.findById(payload.userId);
},
{
payload: { userId: s.params('id') },
}
);
Operation-Level Caching
const getUser = handler(
async ({ payload, cache }) => {
const profile = await cache(
['profile', payload.userId],
async () => {
return await db.profiles.findByUserId(payload.userId);
},
{ ttl: 60000 } // 1 minute TTL
);
const posts = await cache(
['posts', payload.userId],
async () => {
return await db.posts.findByUserId(payload.userId);
},
{ ttl: 30000 } // 30 seconds TTL
);
return { profile, posts };
},
{
payload: { userId: s.params('id') },
cache: {
store,
key: ['user-data'],
},
}
);
Custom Cache Store
import { type CacheStore } from '@apisr/controller';
class RedisStore implements CacheStore {
constructor(private redis: Redis) {}
async get<TValue>(key: string): Promise<TValue | undefined> {
const value = await this.redis.get(key);
return value ? JSON.parse(value) : undefined;
}
async set<TValue>(
key: string,
value: TValue,
options?: { ttl?: number }
): Promise<void> {
const serialized = JSON.stringify(value);
if (options?.ttl) {
await this.redis.setex(key, Math.floor(options.ttl / 1000), serialized);
} else {
await this.redis.set(key, serialized);
}
}
async delete(key: string): Promise<boolean> {
const result = await this.redis.del(key);
return result > 0;
}
}
const redisStore = new RedisStore(redis);
const handler = createHandler({
cache: {
store: redisStore,
},
});
Dynamic Cache Keys
const getOrganizationData = handler(
async ({ payload, cache }) => {
const data = await cache(
['org', payload.orgId, 'data', payload.dataType],
async () => {
return await fetchOrganizationData(payload.orgId, payload.dataType);
}
);
return data;
},
{
payload: {
orgId: s.params('orgId'),
dataType: s.query('type'),
},
cache: {
store,
key: ({ payload }) => ['org', payload.orgId],
},
}
);