Cache Middleware
Response caching middleware for Strata services. Caches successful operation responses and serves them on subsequent requests with matching payloads, skipping the operation handler entirely.
Supports three built-in cache stores (memory, Redis, null) and custom stores via the ICacheStore interface.
Installation
npm install @strata-js/middleware-cachePeer dependency: @strata-js/strata >= 1.4.0 || ^2.0.0
Usage
Create a CacheMiddleware instance with a configuration object, then register it on an operation (or context/service):
import { StrataContext } from '@strata-js/strata';
import { CacheMiddleware } from '@strata-js/middleware-cache';
const ctx = new StrataContext('products');
const cache = new CacheMiddleware({ kind: 'memory', key: 'products' });
ctx.registerOperation('getProduct', async (request) =>
{
return { product: await db.findProduct(request.payload.productId) };
}, [], [ cache ]);On the first request, the handler runs and the response is stored. On subsequent requests with an identical payload, the cached response is returned immediately without calling the handler.
Configuration
Pass a configuration object to the CacheMiddleware constructor. The kind field determines which store is used.
Memory Cache
Uses node-cache for in-process caching. Good for development and single-instance services.
const cache = new CacheMiddleware({
kind: 'memory',
key: 'my-service',
options: {
stdTTL: 3600, // TTL in seconds (default: 86400 = 24 hours)
checkperiod: 120, // Cleanup interval in seconds
},
});| Option | Type | Default | Description |
|---|---|---|---|
kind | 'memory' | -- | Selects the in-memory store. |
key | string | 'cache' | Prefix for cache keys. |
options | NodeCacheOptions | { stdTTL: 86400 } | Passed directly to node-cache. See node-cache options. |
Redis Cache
Uses ioredis for distributed caching across multiple service instances.
const cache = new CacheMiddleware({
kind: 'redis',
key: 'my-service',
options: {
host: 'localhost',
port: 6379,
db: 1,
},
ttl: 60 * 60 * 24, // TTL in seconds
});| Option | Type | Default | Description |
|---|---|---|---|
kind | 'redis' | -- | Selects the Redis store. |
key | string | 'cache' | Prefix for cache keys. |
options | RedisOptions | -- | Passed directly to ioredis. See ioredis options. |
ttl | number | -- | Time-to-live in seconds. If omitted, entries never expire. |
The Redis store includes automatic reconnection with exponential backoff and keepalive (3-minute idle delay).
Null Cache (Disabled)
Disables caching entirely. Useful for turning off caching via configuration without changing code.
const cache = new CacheMiddleware({ kind: 'none' });| Option | Type | Description |
|---|---|---|
kind | 'none' | Selects the null store (all gets return null, all sets are no-ops). |
Custom Cache Stores
Implement the ICacheStore interface to use any backing store:
import { CacheMiddleware } from '@strata-js/middleware-cache';
import type { ICacheStore } from '@strata-js/middleware-cache';
class DynamoStore implements ICacheStore
{
async get(key : string) : Promise<unknown>
{
const item = await dynamo.getItem({ Key: { pk: key } });
return item?.value ?? null;
}
async set(key : string, payload : unknown) : Promise<unknown>
{
await dynamo.putItem({ Key: { pk: key }, value: payload });
return key;
}
// Optional -- called during service shutdown
async teardown() : Promise<void>
{
await dynamo.close();
}
}
const cache = new CacheMiddleware(new DynamoStore(), { kind: 'custom', key: 'my-service' });ICacheStore Interface
interface ICacheStore
{
get(key : string) : Promise<unknown>;
set(key : string, payload : unknown) : Promise<unknown>;
teardown ?: () => Promise<void>;
}| Method | Description |
|---|---|
get(key) | Retrieve a cached value by key. Return null or undefined for a cache miss. |
set(key, payload) | Store a value under the given key. |
teardown() | Optional. Called when the service shuts down -- close connections, flush buffers, etc. |
When using a custom store, pass the store instance as the first argument and a { kind: 'custom' } configuration as the second.
How Caching Works
Cache Key Generation
Cache keys are built from four components, joined by ::
<key>:<context>:<operation>:<sorted-payload-json>For example, a request to products/getProduct with payload { productId: '42' } and key 'my-service' produces:
my-service:products:getProduct:{"productId":"42"}Payload keys are sorted alphabetically before serialization, so { b: 2, a: 1 } and { a: 1, b: 2 } produce identical cache keys.
Request Lifecycle
beforeRequest-- Checks the cache for a matching key. On a hit, callsrequest.succeed()with the cached value (short-circuiting the handler). Setsrequest.cacheHittotrueorfalse.success-- If the request was not a cache hit, stores the response in the cache.failure-- No-op. Failed responses are never cached.teardown-- Callsstore.teardown()if the store implements it (e.g., disconnects Redis).
Examples
Operation-Level Caching
The most common pattern -- cache responses for a single read operation:
import { StrataContext } from '@strata-js/strata';
import { CacheMiddleware } from '@strata-js/middleware-cache';
const products = new StrataContext('products');
const cache = new CacheMiddleware({
kind: 'redis',
key: 'product-service',
options: { host: 'redis.internal', port: 6379 },
ttl: 300, // 5 minutes
});
products.registerOperation('getProduct', async (request) =>
{
return { product: await db.findProduct(request.payload.productId) };
}, [], [ cache ]);Switching Stores by Environment
Use the kind field to swap stores without changing any other code:
import { CacheMiddleware } from '@strata-js/middleware-cache';
import type { CacheMiddlewareConfiguration } from '@strata-js/middleware-cache';
function buildCacheConfig() : CacheMiddlewareConfiguration
{
if(process.env.NODE_ENV === 'production')
{
return {
kind: 'redis',
key: 'my-service',
options: { host: process.env.REDIS_HOST!, port: 6379 },
ttl: 3600,
};
}
if(process.env.DISABLE_CACHE === 'true')
{
return { kind: 'none' };
}
return { kind: 'memory', key: 'my-service' };
}
const cache = new CacheMiddleware(buildCacheConfig());Context-Level Caching
Cache every operation in a context:
const lookups = new StrataContext('lookups');
const cache = new CacheMiddleware({ kind: 'memory', key: 'lookups' });
lookups.useMiddleware(cache);
lookups.registerOperation('getStates', async () => fetchStates());
lookups.registerOperation('getCountries', async () => fetchCountries());