App Caching
Every application gets its own isolated key-value cache, accessible from business rules via the Cache global object. Use it for rate limiting, memoizing expensive computations, or sharing state between rule executions.
// Store a value (TTL optional)Cache.set(key, value, ttl);
// Retrieve a value (null if not found or expired)Cache.get(key);
// Delete a keyCache.delete(key);
// Clear all keys in this app's namespaceCache.clear();
// List keys (with optional prefix pattern)Cache.keys(pattern);TTL formats
Section titled “TTL formats”Cache.set('key', 'value', '5m'); // 5 minutesCache.set('key', 'value', '1h'); // 1 hourCache.set('key', 'value', 300); // 300 secondsCache.set('key', 'value'); // Default TTL (5 minutes)Value types
Section titled “Value types”Cache stores any JSON-serializable value: strings, numbers, booleans, arrays, objects.
Cache.set('count', 42);Cache.set('config', { threshold: 100, enabled: true });Cache.set('tags', ['urgent', 'customer']);Application isolation
Section titled “Application isolation”Caches are namespaced per application. A CRM rule cannot read the HR app’s cache, and vice versa. This is enforced automatically.
Use cases
Section titled “Use cases”Rate limiting
Section titled “Rate limiting”const key = 'api_calls_' + record.user_id;const count = Cache.get(key) || 0;
if (count >= 100) { throw new Error('Rate limit exceeded: max 100 requests per minute');}
Cache.set(key, count + 1, '1m');Memoizing expensive computations
Section titled “Memoizing expensive computations”const cacheKey = 'monthly_stats_' + record.department_id;let stats = Cache.get(cacheKey);
if (stats === null) { // Expensive query const records = list('transactions', { filter: 'and(eq(department_id, "' + record.department_id + '"), gte(date, "2025-01-01"))', limit: 10000 });
stats = { total: records.reduce((sum, r) => sum + r.amount, 0), count: records.length };
Cache.set(cacheKey, stats, '30m');}
record.monthly_total = stats.total;Deduplication
Section titled “Deduplication”const dedupeKey = 'processed_' + record.external_id;if (Cache.get(dedupeKey)) { throw new Error('Duplicate submission');}Cache.set(dedupeKey, true, '1h');Configuration
Section titled “Configuration”| Setting | Default |
|---|---|
| Max keys per app | 1,000 |
| Default TTL | 5 minutes |
| Backend | In-memory or distributed (ValKey) |
| Eviction policy | W-TinyLFU (adaptive frequency/recency) |
Monitoring
Section titled “Monitoring”Superusers can inspect cache stats — hit rate, size, and eviction counts — via the superuser-only API:
GET /api/internal/caches/statsMulti-instance deployments
Section titled “Multi-instance deployments”With the distributed cache enabled, the app cache is shared across instances. Writes on one instance are visible to all others. Without it, each instance has its own in-memory cache.