# FunctionContext cheat sheet

A quick lookup of everything hanging off `context` (Node) / `Context` (C#). For the conceptual tour, see [context object](/docs/functions/functions/concepts/context-object). For method-level detail, trust TypeScript IntelliSense or `dotnet`'s IntelliSense — this page is the "what can I type" reference.

## At a glance

| Capability | Node.js | C# |
|  --- | --- | --- |
| Key-value cache | `context.cache` | `Context.Cache` |
| Blob / file storage | `context.storage` | `Context.Storage` |
| SQLite database | `context.database` (path) | `Context.Database.ConnectionString` |
| Structured logger | `console` | `Context.Logger` |
| Project + function info | `context.config` | `Context.Configuration` |
| Env vars | `context.env` / `process.env` | `Context.Configuration` |
| Request ID | `context.requestId` | `HttpContext.TraceIdentifier` |
| Timestamp | `context.timestamp` | (see request headers) |
| Voice SDK client | `context.voice` | `Context.Voice` |
| Conversation SDK | `context.conversation` | `Context.Conversation` |
| SMS SDK | `context.sms` | `Context.Sms` |
| Numbers SDK | `context.numbers` | `Context.Numbers` |
| Verification SDK (C#) | — | `Context.Verification` |
| Private assets | `context.assets(filename)` | (package helpers) |


SDK client properties are `null` / `undefined` if the required environment variables are missing — always null-check. See [SDK env vars](/docs/functions/reference/sdk-env-vars) for which variables unlock each client.

## Cache


```typescript
// Node
await context.cache.set('session:abc', { userId: 'u1' }, 1800); // TTL seconds
const session = await context.cache.get<{ userId: string }>('session:abc');
const exists = await context.cache.has('session:abc');
await context.cache.extend('session:abc', 600);
await context.cache.delete('session:abc');
const keys = await context.cache.keys('session:*');
const many = await context.cache.getMany<Session>(keys);
```


```csharp
// C#
await Context.Cache.Set("session:abc", new { UserId = "u1" }, 1800);
var session = await Context.Cache.Get<MySession>("session:abc");
if (await Context.Cache.Exists("session:abc"))
    await Context.Cache.Delete("session:abc");
var keys = await Context.Cache.GetKeys();
```

Default TTL is 3600 seconds. Values are JSON-serialized.

## Storage


```typescript
// Node
await context.storage.write('reports/daily.json', JSON.stringify(data));
const buf = await context.storage.read('reports/daily.json');
const files = await context.storage.list('reports/');
const exists = await context.storage.exists('reports/old.json');
await context.storage.delete('reports/old.json');
```


```csharp
// C#
await Context.Storage.WriteAsync("reports/daily.json", JsonSerializer.Serialize(data));
var text = await Context.Storage.ReadTextAsync("reports/daily.json");
var bytes = await Context.Storage.ReadAsync("reports/daily.json");
var files = await Context.Storage.ListAsync("reports/");

// Streams for large files
using var stream = await Context.Storage.ReadStreamAsync("large-file.bin");
```

Keys can include `/` — treat them as folder paths.

## Database


```typescript
// Node — context.database is a file path
import initSqlJs from 'sql.js';
import { readFileSync, writeFileSync, existsSync } from 'fs';

const SQL = await initSqlJs();
const buf = existsSync(context.database) ? readFileSync(context.database) : undefined;
const db = new SQL.Database(buf);
db.run('CREATE TABLE IF NOT EXISTS kv (key TEXT PRIMARY KEY, value TEXT)');
writeFileSync(context.database, Buffer.from(db.export()));
```


```csharp
// C# — Context.Database.ConnectionString is an ADO.NET connection string
using var conn = new SqliteConnection(Context.Database.ConnectionString);
conn.Open();
using var cmd = conn.CreateCommand();
cmd.CommandText = "CREATE TABLE IF NOT EXISTS kv (key TEXT PRIMARY KEY, value TEXT)";
cmd.ExecuteNonQuery();
```

In production, the platform automatically replicates and backs up the database. No code changes required.

## Logger


```typescript
// Node
console.log('Request received', { callId: data.callid });
```


```csharp
// C#
Logger.LogInformation("Request received from {Cli}", data.Cli);
```

Output is captured and streamed via `sinch functions logs`.

## Config and variables


```typescript
// Node — raw
context.config.projectId;
context.config.functionName;
context.config.environment; // 'development' | 'production'
context.config.variables; // object of sinch.json variables

// Node — UniversalConfig helper
import { createConfig } from '@sinch/functions-runtime';
const config = createConfig(context);
config.requireSecret('STRIPE_SECRET_KEY'); // throws if missing
config.getVariable('COMPANY_NAME', 'Acme Corp');
config.getApplicationCredentials(); // voice app key/secret
config.isDevelopment();
config.isProduction();
```


```csharp
// C#
var companyName = Configuration["COMPANY_NAME"] ?? "Acme Corp";
var apiKey = Configuration["STRIPE_SECRET_KEY"]
    ?? throw new InvalidOperationException("STRIPE_SECRET_KEY is required");
```

See [configuration & secrets](/docs/functions/functions/concepts/configuration-secrets) for how the layers fit together.

## Pre-wired SDK clients

Initialized by the runtime when the required env vars are present. See [SDK env vars](/docs/functions/reference/sdk-env-vars) for the full list.


```typescript
// Node — always null-check
if (context.voice) {
  await context.voice.callouts.custom({ ... });
}

if (context.conversation) {
  await context.conversation.messages.send({ ... });
}

if (context.sms) {
  await context.sms.batches.send({ ... });
}
```


```csharp
// C# — same pattern
if (Context.Voice is not null)
    await Context.Voice.Callouts.CustomAsync(...);

if (Context.Conversation is not null)
    await Context.Conversation.Messages.Send(...);
```

## Assets

Private files you ship alongside your code. Do **not** use `fs.readFileSync('./assets/...')` — the deployed artifact structure differs from dev.


```typescript
// Node
const greeting = await context.assets('greetings/en.txt');
const audioBuffer = await context.assets('prompts/welcome.wav');
```


```csharp
// C# — context.Assets helper
var greeting = await Context.Assets("greetings/en.txt");
```

For **public** static files (images, CSS), use the `public/` directory — the runtime serves it at `/`.

## Request metadata


```typescript
// Node
context.requestId; // tracing ID unique to this invocation
context.timestamp; // ISO 8601 when the request entered the runtime
```

Include `context.requestId` in logs and upstream calls for cross-service debugging.

## Custom HTTP request and response (Node custom endpoints only)

For non-voice custom endpoints, the second argument is a `FunctionRequest`:


```typescript
interface FunctionRequest {
  method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
  path: string;
  query?: Record<string, string | string[]>;
  headers?: Record<string, string>;
  body?: unknown;
  params?: Record<string, string>;
}
```

And you return a plain `{ statusCode, body, headers? }` object:


```typescript
return { statusCode: 200, body: { status: 'healthy' } };
return { statusCode: 400, body: { error: 'Missing field: name' } };
return { statusCode: 404, body: { error: 'Not found' } };
```

C# custom endpoints use regular ASP.NET MVC action results (`Ok`, `NotFound`, `BadRequest`, `StatusCode`, etc.).

## Related

- [Context object concept](/docs/functions/functions/concepts/context-object) — narrative tour of the same APIs
- [SDK env vars](/docs/functions/reference/sdk-env-vars) — which variables unlock which client
- [Configuration & secrets](/docs/functions/functions/concepts/configuration-secrets) — how config flows into the runtime
- [Node.js runtime](/docs/functions/functions/runtimes/nodejs) / [C# runtime](/docs/functions/functions/runtimes/csharp) — runtime guides