# FunctionContext cheat sheet A quick lookup of everything hanging off `context` (Node) / `Context` (C#). For the conceptual tour, see [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.log` | `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(keys); ``` ```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"); 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 }); context.log?.info('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](#) 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; headers?: Record; body?: unknown; params?: Record; } ``` 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](#) — narrative tour of the same APIs - [SDK env vars](/docs/functions/reference/sdk-env-vars) — which variables unlock which client - [Configuration & secrets](#) — how config flows into the runtime - [Node.js runtime](#) / [C# runtime](#) — runtime guides