Serverless Functions
DB9 Functions let you deploy JavaScript or TypeScript code that runs inside your database environment. Unlike traditional serverless platforms where you connect to a database over the network, DB9 functions have direct, zero-latency access to your SQL engine and filesystem through the ctx object.
module.exports = { handler: async (input, ctx) => { // Query your database — no connection strings, no drivers const stats = await ctx.db.query(` SELECT count(*) as tables FROM information_schema.tables WHERE table_schema = 'public' `);
// Write a report to the filesystem — in the same function const report = [ `DB9 Function Report — ${new Date().toISOString()}`, `Tables in public schema: ${stats.rows[0][0]}`, `Input received: ${JSON.stringify(input)}`, `Run ID: ${ctx.self.runId}`, ].join("\n"); await ctx.fs9.write("/reports/status.txt", report);
return { tables: Number(stats.rows[0][0]), reportSaved: true, runId: ctx.self.runId }; }};What makes DB9 Functions different:
- Native SQL access —
ctx.db.query()runs queries directly, no connection strings or drivers - Filesystem built in —
ctx.fs9reads and writes files in the same function that queries your database - No infrastructure — deploy with one CLI command, no Dockerfiles or build pipelines
- Secrets management — bind API keys and tokens without hardcoding them
Quick Start
Section titled “Quick Start”1. Write a function
Section titled “1. Write a function”module.exports = { handler: async (input, ctx) => { const greeting = `Hello, ${input?.name || "world"}!`;
// Query the database to show native SQL access const time = await ctx.db.query("SELECT NOW()::text as server_time");
return { greeting, serverTime: time.rows[0][0], runId: ctx.self.runId }; }};2. Deploy
Section titled “2. Deploy”db9 functions create hello --db myapp -f hello.js3. Invoke
Section titled “3. Invoke”db9 functions invoke hello --db myapp --payload '{"name":"Alice"}' --json{ "status": "succeeded", "result_json": "{\"greeting\":\"Hello, Alice!\",\"serverTime\":\"2026-04-01 17:41:31.458000\",\"runId\":\"5e0be3ff-...\"}"}How Functions Work
Section titled “How Functions Work”Architecture
Section titled “Architecture”When you deploy a function, the CLI uploads your code to the DB9 API. Each deploy creates a new version. When you invoke a function, the runtime:
- Loads the active version of your function code
- Creates an isolated execution context with the
ctxobject - Calls your
handler(input, ctx)with the parsed JSON payload - Captures the return value, console output, and execution metadata
- Stores the result as a run record accessible via
historyandlogs
Runtime
Section titled “Runtime”Functions execute in a sandboxed JavaScript runtime. Each invocation is isolated — there is no shared memory or global state between runs. The runtime provides:
- CommonJS module system —
module.exports,require(built-ins only) - Global
fetch()— outbound HTTP (requires allowlist configuration) - Console API —
console.log(),console.error()captured as logs - Standard globals —
Date,JSON,Math,Promise,setTimeout,Buffer,TextEncoder,TextDecoder
Execution Role
Section titled “Execution Role”Functions execute SQL as the authenticated role, not admin. This means:
- Functions cannot access tables unless the
authenticatedrole has been granted privileges - Functions cannot use extensions that require superuser (like fs9 from SQL)
- Functions can use
ctx.fs9— filesystem access is handled through the runtime API, not SQL
Before deploying functions that read or write tables, grant access from the admin role:
-- Grant access to all current tablesGRANT ALL ON ALL TABLES IN SCHEMA public TO authenticated;GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO authenticated;
-- Grant access to future tables created by adminALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO authenticated;Writing Functions
Section titled “Writing Functions”JavaScript
Section titled “JavaScript”module.exports = { handler: async (input, ctx) => { const result = await doWork(input); return { status: "ok", result }; }};TypeScript
Section titled “TypeScript”module.exports = { handler: async (input: Record<string, unknown>, ctx: any) => { const value: number = 42; return { result: value * 2, type: typeof value }; }};The CLI auto-detects .ts files and transpiles them before deploying. Type annotations are stripped — there is no type checking at deploy time.
To pipe TypeScript from stdin:
cat handler.ts | db9 functions create my-func --db myapp --tsFunction Signature
Section titled “Function Signature”handler(input: object | null, ctx: Context) => Promise<object> | object| Parameter | Type | Description |
|---|---|---|
input | object | null | Parsed JSON from --payload (CLI) or input field (API). null if no payload provided. |
ctx | Context | Runtime context: ctx.db, ctx.fs9, ctx.self. See Runtime & ctx. |
| Return | object | Any JSON-serializable value. Stored as result_json in the run record. |
If the function throws, the run status is failed with error_code: "execution_error" and the error message captured in error_message.
Logging
Section titled “Logging”Use console.log() and console.error() inside your function. Output is captured and retrievable:
db9 functions logs my-func <run_id> --db myappDeploying
Section titled “Deploying”Create a function
Section titled “Create a function”# From a filedb9 functions create my-func --db myapp -f index.js
# From stdin (pipe bundled output)cat dist/index.js | db9 functions create my-func --db myapp -f -
# With TypeScriptdb9 functions create my-func --db myapp -f index.ts
# With filesystem accessdb9 functions create my-func --db myapp -f index.js \ --fs9-scope /data:ro --fs9-scope /output:rw
# With secretsdb9 functions create my-func --db myapp -f index.js \ --secret API_KEY=my_api_key
# With execution limitsdb9 functions create my-func --db myapp -f index.js \ --timeout 60000 \ --limits-json '{"timeout_ms":60000,"memory_mb":256}'Update a function
Section titled “Update a function”Each update creates a new version. The active version is updated immediately.
db9 functions update my-func --db myapp -f index.jsdb9 functions update my-func --db myapp --timeout 60000db9 functions update my-func --db myapp --secret NEW_KEY=new_secretList functions
Section titled “List functions”db9 functions list --database myappdb9 functions list --database myapp --jsonInvoking
Section titled “Invoking”Via CLI
Section titled “Via CLI”db9 functions invoke my-func --db myappdb9 functions invoke my-func --db myapp --payload '{"key":"value"}'db9 functions invoke my-func --db myapp --jsonThe response includes the run status, result, and version:
{ "function_id": "1245b49d-...", "run_id": "d3dc72c8-...", "status": "succeeded", "result_json": "{\"message\":\"hello\"}", "version_id": "7df52fc3-..."}Via REST API
Section titled “Via REST API”curl -X POST \ -H "Authorization: Bearer $DB9_API_KEY" \ -H "Content-Type: application/json" \ -d '{"input": {"key": "value"}}' \ https://api.db9.ai/customer/databases/{database_id}/functions/{function_id}/invokeSee REST API — Functions for the full endpoint reference.
Next Steps
Section titled “Next Steps”- Runtime & ctx — ctx.db, ctx.fs9, ctx.self — the function runtime API
- Configuration — Secrets, fs9 scope, network, cron, limits
- Examples — Real-world patterns: webhooks, ETL, APIs
- Troubleshooting — Errors, logs, debugging, limitations
- CLI — Functions — Full CLI command reference
- REST API — Functions — Deploy, invoke, and manage via API