Configuration
Secrets
Section titled “Secrets”Secrets let you pass sensitive values (API keys, tokens, connection strings) to functions without embedding them in code.
Create a secret
Section titled “Create a secret”# From stdin (recommended for CI/CD)echo -n "sk-abc123" | db9 functions secrets set MY_API_KEY --db myapp --value-stdin
# Inline (visible in shell history — not recommended)db9 functions secrets set MY_API_KEY --db myapp --value "sk-abc123"Bind secrets to a function
Section titled “Bind secrets to a function”The --secret flag maps an alias (used in your code) to a secret name (stored in the database):
db9 functions create my-func --db myapp -f index.js \ --secret API_KEY=MY_API_KEY \ --secret DB_TOKEN=external_db_tokenAccess secrets in code
Section titled “Access secrets in code”module.exports = { handler: async (input, ctx) => { const apiKey = ctx.secrets.get("API_KEY");
const response = await fetch("https://api.example.com/data", { headers: { "Authorization": `Bearer ${apiKey}` }, });
return await response.json(); }};Manage secrets
Section titled “Manage secrets”# List secrets (names only — values are never shown)db9 functions secrets list --db myapp
# Update (set uses create-or-update semantics)db9 functions secrets set MY_API_KEY --db myapp --value "new-value"
# Deletedb9 functions secrets delete MY_API_KEY --db myappFilesystem Access (fs9)
Section titled “Filesystem Access (fs9)”Functions can read and write files in the database’s filesystem. Access is controlled by --fs9-scope flags at deploy time:
db9 functions create my-func --db myapp -f index.js \ --fs9-scope /data:ro \ --fs9-scope /output:rw| Mode | Description |
|---|---|
ro | Read-only access to the path and its children |
rw | Read-write access to the path and its children |
Scopes are enforced, and they are the only way in: a function created without any --fs9-scope
cannot use ctx.fs9 at all. Every call — even reading a file that exists — fails with
FS9 resource was not found, the same message as a genuinely missing path. With scopes declared:
| Call | Error |
|---|---|
| Path outside every scope | Path '/reports/a.txt' is outside all declared fs9 scopes |
Write under an ro scope | Operation 'write' is not allowed on scope '/data' (mode: ro) |
In the CLI only db9 functions create sets scopes; db9 functions update has no --fs9-scope
flag and keeps the scopes the function already has. To change them later, send fs9_scope with
PUT /customer/databases/{db_id}/functions/{fn_id}, which also needs the source as bundle_ref
(see REST API — Functions).
Reading files
Section titled “Reading files”// Read as UTF-8 stringconst text = await ctx.fs9.read("/data/config.json");const config = JSON.parse(text);
// Read as base64 (for binary files like images, xlsx)const base64 = await ctx.fs9.readBase64("/data/image.png");const buffer = Buffer.from(base64, "base64");Writing files
Section titled “Writing files”await ctx.fs9.write("/output/report.csv", csvContent);await ctx.fs9.write("/output/data.json", JSON.stringify(results, null, 2));write() replaces an existing file and does not create missing parent directories: writing
into a directory that does not exist fails with FS9 resource was not found, and ctx.fs9 has no
mkdir. Create the directory once before the first run:
db9 db sql myapp -q "SELECT fs9_mkdir('/output', true)"db9 fs cp creates parent directories as it uploads, so uploading an input file to /data/...
also creates /data.
Listing and inspecting
Section titled “Listing and inspecting”const entries = await ctx.fs9.list("/data/");// Returns: [{ path, type, size, mtime, ... }, ...]
const stat = await ctx.fs9.stat("/data/file.txt");// Returns: { path, type: "file", size: 1024, mtime: "2026-04-01T...",// generation: ..., mode: 420, sealed: false }Deleting
Section titled “Deleting”await ctx.fs9.delete("/output/old-report.csv");Network Access (fetch)
Section titled “Network Access (fetch)”Functions have access to the global fetch() API for outbound HTTP requests. Network access is blocked by default — only URLs on the allowlist can be reached.
module.exports = { handler: async (input, ctx) => { const response = await fetch("https://api.example.com/data", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ query: input.query }), });
if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); }
return await response.json(); }};If a URL is not in the allowlist, fetch() throws with:
EACCES: permission denied, connect 'https://...': url not in allowlistCron Scheduling
Section titled “Cron Scheduling”A function can run on a schedule, which is useful for periodic data cleanup, report generation,
and automated maintenance. Set it with the cron_schedule field of the
REST API; the CLI has no flag for it.
Schedule via the REST API
Section titled “Schedule via the REST API”A scheduled function must use "run_as": "service" — with invoker, or without run_as, the
request fails with 400 cron_requires_service. Set both on create, or on an update of an existing
function (an update must resend the source as bundle_ref):
curl -X PUT \ -H "Authorization: Bearer $DB9_API_KEY" \ -H "Content-Type: application/json" \ -d '{"bundle_ref": "inline:module.exports = { handler: async (input, ctx) => ({ ok: 1 }) }", "run_as": "service", "cron_schedule": "0 3 * * *"}' \ https://api.db9.ai/customer/databases/{db_id}/functions/{function_id}The scheduler checks every minute and skips a run while the previous one is still going. Each
scheduled run is recorded with trigger_type cron and run_as service.
A service function runs SQL as the service role, not authenticated, so it needs its own
grants — without them its queries fail with permission denied for table ... (42501):
GRANT ALL ON ALL TABLES IN SCHEMA public TO service;GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO service;Schedule via SQL (pg_cron)
Section titled “Schedule via SQL (pg_cron)”cron.schedule() can also invoke a function’s REST endpoint on a recurring schedule, once job
scheduling ships:
SELECT cron.schedule( 'nightly-cleanup', '0 3 * * *', $$SELECT http_post( 'https://api.db9.ai/customer/databases/{db_id}/functions/{function_id}/invoke', '{"input": {}}', 'application/json', jsonb_build_object('Authorization', 'Bearer ' || current_setting('app.api_token')) )$$);Cron expression format
Section titled “Cron expression format”cron_schedule takes five fields — minute, hour, day of month, month, day of week (0 is Sunday)
— evaluated in UTC. Each field accepts *, a number, a range (1-5), a step (*/5, 1-30/5) or
a comma-separated list of these. Names such as MON are rejected with 400 invalid_cron_schedule.
| Expression | Meaning |
|---|---|
*/5 * * * * | Every 5 minutes |
0 * * * * | Every hour |
0 3 * * * | Daily at 3:00 AM UTC |
0 0 * * 0 | Weekly on Sunday at midnight |
0 12 1 * * | Monthly on the 1st at noon |
See Scheduled Jobs with pg_cron for the complete scheduling guide.
Versioning
Section titled “Versioning”Every db9 functions update creates a new version. The active version is updated immediately — there is no staged deployment.
# Deploy version 1db9 functions create my-func --db myapp -f v1.js
# Deploy version 2 (replaces version 1 immediately)db9 functions update my-func --db myapp -f v2.jsEach run record stores the version_id that was used, so you can trace which version produced a given result:
db9 functions history my-func --db myapp -n 5 --jsonThere is currently no built-in rollback command. To revert, re-deploy the previous version’s code:
db9 functions update my-func --db myapp -f previous-version.jsEach version also records its result format. An update without --db-api
keeps the active version’s format, so when you re-deploy code written for the other format, pass
--db-api as well.
Execution Limits
Section titled “Execution Limits”| Limit | Default | Max | Description |
|---|---|---|---|
timeout_ms | 30,000 (30s) | 300,000 (5 min) | Maximum execution time |
memory_mb | System default | — | Memory limit |
max_db_queries | System default | — | SQL queries per invocation |
max_db_rows | System default | — | Total rows returned across queries |
max_outbound_requests | System default | — | Outbound HTTP requests |
max_response_bytes | System default | — | Max size per HTTP response |
network_timeout_ms | System default | — | Per-request network timeout |
max_fs9_requests | System default | — | fs9 API calls per invocation |
max_fs9_read_bytes | System default | — | Total bytes read from fs9 |
max_fs9_write_bytes | System default | — | Total bytes written to fs9 |
Set limits via flags or JSON:
# Convenience flag (timeout only)db9 functions create my-func --db myapp -f index.js --timeout 60000
# Full limits JSONdb9 functions create my-func --db myapp -f index.js \ --limits-json '{"timeout_ms":60000,"memory_mb":256,"max_db_queries":100}'
# From filedb9 functions create my-func --db myapp -f index.js \ --limits-file ./limits.jsonVisibility
Section titled “Visibility”Functions have a visibility field that defaults to private. This controls whether the function can be invoked via a publishable key (public endpoint) without authentication.
Set it with --visibility private|public on db9 functions create or db9 functions update.
Query Result Format
Section titled “Query Result Format”--db-api (CLI 2.2.5) on db9 functions create or db9 functions update sets what
ctx.db.query() returns, and the API takes the same choice as the db_api field:
| Value | ctx.db.query() returns |
|---|---|
sql-api (default) | { columns, rows, row_count, command }, with rows as arrays |
pg | A node-postgres result (rows as objects, rowCount, fields), and node-postgres errors with err.code |
db9 functions create my-func --db myapp -f index.js --db-api pgThe CLI rejects any other value as a usage error, and the API with 400 invalid_db_api. The
db9 functions list output does not show the format; the version.db_api field of the
create/update --json output does. See Runtime & ctx
for the value types in each format.
Next Steps
Section titled “Next Steps”- Overview — Quick start and deployment basics
- Runtime & ctx — ctx.db, ctx.fs9, ctx.self API
- Examples — Real-world patterns
- Troubleshooting — Errors, logs, debugging