# Context object Every handler receives a `FunctionContext` as its first argument (Node) or as an injected dependency (C#). Think of it as the standard library of the Sinch runtime — everything you need to interact with state, configuration, and the rest of Sinch is hanging off of it. ## What's in it Both runtimes expose the same capabilities with idiomatic names. | 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` (conn) | | Structured logger | `context.log` / `console` | `Context.Logger` | | Project + function info | `context.config` | `Context.Configuration` | | Env vars | `context.env` | `Context.Configuration` | | Request metadata | `context.requestId`, `.timestamp` | Available via `HttpContext` | | Pre-wired Voice SDK | `context.voice` | `Context.Voice` | | Pre-wired Conversation SDK | `context.conversation` | `Context.Conversation` | | Pre-wired SMS SDK | `context.sms` | `Context.Sms` | | Pre-wired Numbers SDK | `context.numbers` | `Context.Numbers` | | Pre-wired Verification SDK | — | `Context.Verification` | | Read private assets | `context.assets(filename)` | Package-level helpers | The SDK client properties are `null` / `undefined` if the corresponding environment variables are not set — see [configuration & secrets](#) for which vars unlock which client. ## Cache Key-value store with TTL. Use it for per-call state (keyed by `callid`), rate limiting, session data, and anything you want to survive between callbacks without a full database trip. - **Dev:** in-memory. Lost on restart. - **Prod:** persistent, shared across invocations, distributed. ```typescript // Node await context.cache.set('session:abc', { userId: 'u1' }, 1800); const session = await context.cache.get<{ userId: string }>('session:abc'); await context.cache.extend('session:abc', 600); await context.cache.delete('session:abc'); const keys = await context.cache.keys('session:*'); ``` ```csharp // C# await Context.Cache.Set("session:abc", new { UserId = "u1" }, 1800); var session = await Context.Cache.Get("session:abc"); if (await Context.Cache.Exists("session:abc")) await Context.Cache.Delete("session:abc"); ``` Default TTL is 3600 seconds. Values are JSON-serialized. ## Storage Blob storage for persistent files — call recordings, reports, user uploads, arbitrary data. - **Dev:** local filesystem in `./storage/`. - **Prod:** durable cloud storage with a local read cache. ```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 files = await Context.Storage.ListAsync("reports/"); // Streaming for big files using var stream = await Context.Storage.ReadStreamAsync("large-file.bin"); ``` Keys can include path separators — treat them like folder paths. ## Database A per-function SQLite database. You bring your own SQLite library; the runtime manages the file location and replication. - **Dev:** plain local SQLite file in `./data/`. - **Prod:** SQLite with automatic durable replication. The database survives restarts and scale events, with no code changes required. ### Node — `sql.js` (recommended) Pure JavaScript SQLite compiled to WebAssembly. No native compilation. ```typescript 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)'); db.run('INSERT OR REPLACE INTO kv VALUES (?, ?)', ['greeting', 'hello']); writeFileSync(context.database, Buffer.from(db.export())); db.close(); ``` `better-sqlite3` is faster but needs native compilation — use it when you can guarantee the build environment has `python3` / `make` / `g++`. ### C# — `Microsoft.Data.Sqlite` ```csharp using var conn = new SqliteConnection(Context.Database.ConnectionString); conn.Open(); using var cmd = conn.CreateCommand(); cmd.CommandText = "CREATE TABLE IF NOT EXISTS call_log (id INTEGER PRIMARY KEY, caller TEXT, ts INTEGER)"; cmd.ExecuteNonQuery(); ``` Or with Dapper: ```csharp using var conn = new SqliteConnection(Context.Database.ConnectionString); var calls = await conn.QueryAsync("SELECT * FROM call_log ORDER BY ts DESC LIMIT 10"); ``` ## Config and variables `context.config` exposes project and function metadata — useful for logging, feature flags by environment, and constructing return URLs. ```typescript context.config.projectId // your Sinch Project ID context.config.functionName // e.g. 'my-ivr' context.config.environment // 'development' | 'production' context.config.variables // object of sinch.json variables ``` For a richer API, use `createConfig(context)` (aka `createUniversalConfig`): ```typescript import { createConfig } from '@sinch/functions-runtime'; const config = createConfig(context); const apiKey = config.requireSecret('STRIPE_SECRET_KEY'); // throws if missing const company = config.getVariable('COMPANY_NAME', 'Acme Corp'); if (config.isProduction()) { /* ... */ } ``` In C#, use the injected `IConfiguration`: ```csharp var companyName = Configuration["COMPANY_NAME"] ?? "Acme Corp"; var apiKey = Configuration["STRIPE_SECRET_KEY"] ?? throw new InvalidOperationException("STRIPE_SECRET_KEY is required"); ``` ## Pre-wired Sinch SDK clients The runtime instantiates Sinch SDK clients for you and attaches them to the context. No credential management in your function. | Client | Node | C# | Env vars required | | --- | --- | --- | --- | | Voice | `context.voice` | `Context.Voice` | `VOICE_APPLICATION_KEY`, `VOICE_APPLICATION_SECRET` | | Conversation | `context.conversation` | `Context.Conversation` | `CONVERSATION_APP_ID` | | SMS | `context.sms` | `Context.Sms` | `SMS_SERVICE_PLAN_ID` | | Numbers | `context.numbers` | `Context.Numbers` | `ENABLE_NUMBERS_API=true` | | Verification (C#) | — | `Context.Verification` | `VERIFICATION_APPLICATION_ID`, `VERIFICATION_APPLICATION_SECRET` | All clients require the base Project credentials: `PROJECT_ID`, `PROJECT_ID_API_KEY`, `PROJECT_ID_API_SECRET`. If a required variable is missing, the property is `null` — always null-check before using: ```typescript if (context.conversation) { await context.conversation.messages.send({ ... }); } ``` ```csharp if (Context.Conversation is not null) await Context.Conversation.Messages.Send(...); ``` ## Assets Private files you ship alongside your code — prompts, JSON data, templates. Keep them in the `assets/` directory and read them with `context.assets(filename)`. Do **not** use `fs.readFileSync('./assets/...')` — the deployed artifact structure is different from your dev tree, and the runtime handles the translation for you. ```typescript const greetingText = await context.assets('greetings/en.txt'); ``` For public static files (images, CSS, public JSON), use the `public/` directory instead. The runtime serves it at `/`. ## Logger `context.log` (Node) and `Context.Logger` (C#) are structured loggers. In production, output is captured, indexed, and streamed via `sinch functions logs`. ```typescript context.log?.info('Call received', { callId: data.callid, cli: data.cli }); ``` ```csharp Logger.LogInformation("Call received from {Cli}", data.Cli); ``` In Node, `console.log` also works — it's captured the same way as `context.log`. ## Request metadata - `context.requestId` — a tracing ID unique to this invocation. Include it in logs and when calling out to other services; it makes cross-service debugging bearable. - `context.timestamp` — ISO 8601 timestamp when the request entered the runtime. ## Related - [Configuration & secrets](#) — how env vars, `sinch.json` variables, and secrets flow - [Local vs prod](#) — what changes between dev and deployed environments - [Function context reference](/docs/functions/reference/function-context) — Node + C# side-by-side cheat sheet