Skip to content
Discord Get Started

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.

index.js
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 accessctx.db.query() runs queries directly, no connection strings or drivers
  • Filesystem built inctx.fs9 reads 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
hello.js
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
};
}
};
Terminal
db9 functions create hello --db myapp -f hello.js
Terminal
db9 functions invoke hello --db myapp --payload '{"name":"Alice"}' --json
JSON
{
"status": "succeeded",
"result_json": "{\"greeting\":\"Hello, Alice!\",\"serverTime\":\"2026-04-01 17:41:31.458000\",\"runId\":\"5e0be3ff-...\"}"
}

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:

  1. Loads the active version of your function code
  2. Creates an isolated execution context with the ctx object
  3. Calls your handler(input, ctx) with the parsed JSON payload
  4. Captures the return value, console output, and execution metadata
  5. Stores the result as a run record accessible via history and logs

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 systemmodule.exports, require (built-ins only)
  • Global fetch() — outbound HTTP (requires allowlist configuration)
  • Console APIconsole.log(), console.error() captured as logs
  • Standard globalsDate, JSON, Math, Promise, setTimeout, Buffer, TextEncoder, TextDecoder

Functions execute SQL as the authenticated role, not admin. This means:

  • Functions cannot access tables unless the authenticated role 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:

SQL
-- Grant access to all current tables
GRANT 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 admin
ALTER DEFAULT PRIVILEGES IN SCHEMA public
GRANT ALL ON TABLES TO authenticated;
index.js
module.exports = {
handler: async (input, ctx) => {
const result = await doWork(input);
return { status: "ok", result };
}
};
index.ts
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:

Terminal
cat handler.ts | db9 functions create my-func --db myapp --ts
handler(input: object | null, ctx: Context) => Promise<object> | object
ParameterTypeDescription
inputobject | nullParsed JSON from --payload (CLI) or input field (API). null if no payload provided.
ctxContextRuntime context: ctx.db, ctx.fs9, ctx.self. See Runtime & ctx.
ReturnobjectAny 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.

Use console.log() and console.error() inside your function. Output is captured and retrievable:

Terminal
db9 functions logs my-func <run_id> --db myapp
Terminal
# From a file
db9 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 TypeScript
db9 functions create my-func --db myapp -f index.ts
# With filesystem access
db9 functions create my-func --db myapp -f index.js \
--fs9-scope /data:ro --fs9-scope /output:rw
# With secrets
db9 functions create my-func --db myapp -f index.js \
--secret API_KEY=my_api_key
# With execution limits
db9 functions create my-func --db myapp -f index.js \
--timeout 60000 \
--limits-json '{"timeout_ms":60000,"memory_mb":256}'

Each update creates a new version. The active version is updated immediately.

Terminal
db9 functions update my-func --db myapp -f index.js
db9 functions update my-func --db myapp --timeout 60000
db9 functions update my-func --db myapp --secret NEW_KEY=new_secret
Terminal
db9 functions list --database myapp
db9 functions list --database myapp --json
Terminal
db9 functions invoke my-func --db myapp
db9 functions invoke my-func --db myapp --payload '{"key":"value"}'
db9 functions invoke my-func --db myapp --json

The response includes the run status, result, and version:

JSON
{
"function_id": "1245b49d-...",
"run_id": "d3dc72c8-...",
"status": "succeeded",
"result_json": "{\"message\":\"hello\"}",
"version_id": "7df52fc3-..."
}
Terminal
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}/invoke

See REST API — Functions for the full endpoint reference.