Skip to Content
DocsExecution PoliciesCaching & Invalidation

Caching & Invalidation

Actyx RPC includes powerful caching policies supporting request deduplication, stale-while-revalidate, Redis storage, and automatic key invalidation on mutation.


.cache()

Add caching to your queries to cache successful results and avoid repeated database or network calls.

import { MemoryCache } from "@explita/actyx-rpc"; const procedure = createProcedure({ cache: new MemoryCache({ maxSize: 1000, defaultTTL: 60000 }), }); const getUser = procedure .cache({ ttl: 60000, // Cache for 60 seconds staleTime: 30000, // Data becomes stale after 30 seconds staleWhileRevalidate: true, // Return stale data while revalidating in the background key: (input) => `user:${input.id}`, }) .input(z.object({ id: z.string() })) .query(async ({ input }) => { return await db.users.findById(input.id); });

Cache Configuration Options

OptionTypeDefaultDescription
ttlnumber60000Cache time-to-live in milliseconds.
staleTimenumber0Delay in ms before data is considered stale. 0 means always stale.
staleWhileRevalidatebooleanfalseReturn stale cache immediately and fetch fresh data in the background.
key(input) => stringJSON.stringifyCustom key generator function.
onHit(key, data) => voidCallback run on cache hit.
onMiss(key) => voidCallback run on cache miss.
decompressbooleanfalseSet to true if response payload compression was configured.

Cache Adapters

Redis Cache Adapter

For distributed systems, configure a Redis adapter inside createProcedure:

import Redis from "ioredis"; import { RedisCache, createProcedure } from "@explita/actyx-rpc"; const redis = new Redis({ host: "localhost", port: 6379 }); const redisCache = new RedisCache(redis, { prefix: "myapp:cache:", defaultTTL: 300, // seconds }); const procedure = createProcedure({ cache: redisCache, });

Custom Cache Adapter

To implement a custom cache (e.g., memcached, cloudflare KV), implement the CacheAdapter interface:

interface CacheAdapter { get<T>(key: string): Promise<T | undefined> | T | undefined; set<T>( key: string, data: T, options?: { ttl?: number; staleTime?: number }, ): Promise<void> | void; isStale(key: string): Promise<boolean> | boolean; delete(key: string): Promise<boolean> | boolean; clear(): Promise<void> | void; }

.invalidate()

Trigger cache invalidation automatically after a mutation completes successfully.

const updatePost = procedure .input(z.object({ id: z.string(), title: z.string() })) .invalidate({ keys: ({ input }) => [`post:${input.id}`, "posts:list"], tags: ["posts"], }) .mutation(async ({ input }) => { // ... update database return { success: true }; });

Invalidation Options

OptionTypeDescription
keysstring | string[] | (opts) => string[]Specific cache key(s) to delete.
patternsstring | string[] | (opts) => string[]Glob patterns to match keys (if supported by cache adapter).
tagsstring | string[] | (opts) => string[]Specific cache tag(s) to invalidate.
delaynumberDelay in ms before invalidation runs.

Combining Cache & Retry

When .cache() and .retry() are used together, the cache wraps the retry layer. This means if a valid cached result exists, execution stops immediately and the retry logic is never reached. Only on a cache miss (or stale entry requiring recomputation) does the flow continue into retry and then the handler.

const getUser = procedure .retry({ attempts: 3 }) // Retry on failure .cache({ ttl: 60000 }) // Cache successful results .query(async ({ input }) => { return await db.users.findById(input.id); });

[!NOTE] Input validation and middleware always run before the cache check. This ensures auth and rate limiting apply to all requests, including cached ones. The validation overhead is minimal.


Execution Flow

The following flows illustrate the execution path and ordering of context, rate-limiting, resolvers, caching, and handler execution.

1. Normal Request (Cache Hit)

If a valid cached result exists, execution returns early. Retries, timeouts, and handlers are never reached.

2. Normal Request (Cache Miss)

On a cache miss, the execution chain proceeds through retries and timeouts down to the actual handler.

3. Stale Cache (Revalidation Case)

If staleWhileRevalidate is set to true, a stale value is returned to the user immediately, and recomputation triggers asynchronously in the background.

Last updated on