Skip to content

Working with the Cache

Every Sinch Function has access to a shared key-value cache. In development it uses in-memory storage. In production, the cache is persistent across restarts and shared across all instances of your function.

Interface

OperationNode.jsC#
Writecache.set(key, value, ttl?)cache.Set(key, value, ttl?)
Readcache.get<T>(key)cache.Get<T>(key)
Check existencecache.has(key)cache.Exists(key)
Deletecache.delete(key)cache.Delete(key)
Extend TTLcache.extend(key, seconds)
List keyscache.keys(pattern?)cache.GetKeys()
Batch readcache.getMany(keys)

Default TTL: 3600 seconds (1 hour).

Accessing the cache

import type { FunctionContext, FunctionRequest } from '@sinch/functions-runtime';

export async function myHandler(context: FunctionContext, request: FunctionRequest) {
  const cache = context.cache;
  await cache.set('greeting', 'Hello!');
  const value = await cache.get<string>('greeting'); // 'Hello!'
}

TTL (time to live)

await cache.set('short-lived', 'value', 60); // 60 seconds
await cache.set('persistent', 'value', 86400 * 30); // 30 days
await cache.extend('session:abc123', 300); // extend by 5 minutes

Storing objects

Values are JSON-serialized automatically.

interface UserProfile {
  name: string;
  plan: string;
  createdAt: string;
}

await cache.set<UserProfile>(
  'user:123',
  {
    name: 'Alice',
    plan: 'pro',
    createdAt: new Date().toISOString(),
  },
  3600
);

const profile = await cache.get<UserProfile>('user:123');

Common patterns

Session storage

Store caller state across voice callbacks using the call ID as key.

// In ICE handler
await context.cache.set(
  `session:${event.callId}`,
  {
    callerNumber: event.cli,
    startTime: Date.now(),
  },
  600
);

// In PIE handler
const session = await context.cache.get<{ callerNumber: string; startTime: number }>(
  `session:${event.callId}`
);

Rate limiting

async function isRateLimited(
  cache: IFunctionCache,
  caller: string,
  maxPerHour = 10
): Promise<boolean> {
  const key = `rate:${caller}`;
  const current = await cache.get<number>(key);
  if (current === null) {
    await cache.set(key, 1, 3600);
    return false;
  }
  if (current >= maxPerHour) return true;
  await cache.set(key, current + 1, 3600);
  return false;
}

Caller lookup cache

async function getCustomer(cache: IFunctionCache, phone: string) {
  const cacheKey = `customer:${phone}`;
  const cached = await cache.get<Customer>(cacheKey);
  if (cached) return cached;

  const customer = await fetchCustomerFromApi(phone);
  if (customer) await cache.set(cacheKey, customer, 300);
  return customer;
}

Development vs production

AspectDevelopmentProduction
BackendIn-memoryPersistent store
PersistenceLost on restartSurvives restarts
SharingSingle process onlyShared across all instances
TTL enforcementApproximateExact

The IFunctionCache interface is identical in both environments. No code changes needed when deploying.