Skip to main content

Functions

Where Methods let you wrap SQL in a named, callable unit, Functions let you do the same with code. A function is a named piece of Node.js or Python that Centia.io runs for you — on demand, on a schedule, or when a table changes — inside an isolated sandbox.

Why use functions?​

  • Custom logic: do things SQL can't — call an external API, transform a file, send a notification, compute something bespoke.
  • Secure by default: your code runs in a sandbox, isolated from other tenants.
  • First-class data access: each invocation gets a short-lived, scoped token so your handler can call the Centia.io API back with your own privileges — rules and geofencing still apply.
  • Event-driven: trigger a function from a cron schedule or a row change.
  • Typed clients: infer input/output types from a dry-run and generate a TypeScript interface.

A function receives two arguments: event (the payload you invoke it with) and context (execution info, including a scoped token and the API apiBaseUrl).

The handler​

The handler field points at an exported function as file.export. The default index.handler means "the handler export of index".

index.mjs
export const handler = async (event, context) => {
return { greeting: `Hello, ${event.name}!` };
};

Create a function​

Post a function definition. code is the inline source (see Packaging for multi-file zips).

POST https://api.centia.io/api/v4/functions
Content-Type: application/json
Authorization: Bearer abc123

{
"name": "greet",
"runtime": "nodejs20",
"handler": "index.handler",
"code": "export const handler = async (event) => ({ greeting: `Hello, ${event.name}!` });",
"memory_mb": 128,
"timeout_s": 30
}

Fields:

  • name — unique, a valid identifier ([A-Za-z_][A-Za-z0-9_]*).
  • runtime — nodejs20 or python312.
  • handler — entry point as file.export, e.g. index.handler.
  • code — inline source, or a base64 zip when package is zip.
  • package — inline (default) or zip.
  • env — environment variables passed to the runtime.
  • memory_mb (default 128), timeout_s (default 30).
  • triggers — schedule and/or event bindings (see Triggers).

Invoke a function​

POST an event payload to the function's invocations sub-resource. By default this is synchronous — the handler runs and the result is returned.

POST https://api.centia.io/api/v4/functions/greet/invocations
Content-Type: application/json
Authorization: Bearer abc123

{ "name": "Ada" }

Response:

{
"invocation": "b1f0…",
"status": "succeeded",
"result": { "greeting": "Hello, Ada!" },
"logs": "",
"duration_ms": 12
}

Asynchronous invocation​

Add ?async=true (or the header X-Invocation-Type: Event) to queue the invocation and return immediately with 202 Accepted. A background worker runs it; poll the invocation to get the result.

POST https://api.centia.io/api/v4/functions/greet/invocations?async=true
Content-Type: application/json
Authorization: Bearer abc123

{ "name": "Ada" }
{ "invocation": "b1f0…", "status": "pending" }

Check status:

GET https://api.centia.io/api/v4/functions/greet/invocations/b1f0… HTTP/1.1
Authorization: Bearer abc123
{ "invocation": "b1f0…", "function": "greet", "status": "succeeded", "response": { "greeting": "Hello, Ada!" } }

Calling Centia.io from your function​

Every invocation receives a short-lived, scoped token in context.token and the API base URL in context.apiBaseUrl. Use them to call the Centia.io API back — the request runs with your privileges, so rules and geofencing are enforced automatically.

index.mjs
export const handler = async (event, context) => {
const res = await fetch(`${context.apiBaseUrl}/api/v4/sql`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${context.token}`,
},
body: JSON.stringify({ q: "SELECT count(*) AS n FROM my_table" }),
});
return await res.json();
};

Triggers​

Functions can run automatically. Add a triggers object when creating or updating a function.

Schedule​

Run on a cron schedule (five-field cron expression).

POST https://api.centia.io/api/v4/functions
Content-Type: application/json
Authorization: Bearer abc123

{
"name": "nightlyReport",
"runtime": "python312",
"handler": "index.handler",
"code": "def handler(event, context):\n return {'ran': True}\n",
"triggers": { "schedule": "0 2 * * *" }
}

The event for a scheduled run is { "source": "schedule", "time": "…", "cron": "…" }.

Table events​

Run when rows change in a table. on may contain any of insert, update, delete (defaults to all).

POST https://api.centia.io/api/v4/functions
Content-Type: application/json
Authorization: Bearer abc123

{
"name": "onOrder",
"runtime": "nodejs20",
"handler": "index.handler",
"code": "export const handler = async (event) => { console.log(event.op, event.pk); };",
"triggers": { "event": { "table": "public.orders", "on": ["insert", "update"] } }
}

The event for a table change looks like:

{
"source": "db",
"op": "insert",
"schema": "public",
"table": "orders",
"pk_column": "id",
"pk": "42",
"row": { "id": 42, "total": 199 }
}

The watched table must have a single-column primary key. Event functions run asynchronously and inherit the privileges of the user who created the function.

Packaging: multi-file bundles​

For anything beyond a single file — helper modules, bundled dependencies — set package to zip and send a base64-encoded zip archive as code. The archive is extracted and the handler entry file is resolved inside it (Node resolves relative and node_modules imports; Python can import sibling modules).

POST https://api.centia.io/api/v4/functions
Content-Type: application/json
Authorization: Bearer abc123

{
"name": "report",
"runtime": "nodejs20",
"handler": "index.handler",
"package": "zip",
"code": "UEsDBBQAAAAI…(base64 zip)…"
}

Typed clients (dry-run + TypeScript)​

Functions are opaque code, so their input/output types are learned from a dry-run: invoke once with a representative event and Centia.io infers and stores the input and output type shapes.

POST https://api.centia.io/api/v4/functions/greet/invocations?dry=true
Content-Type: application/json
Authorization: Bearer abc123

{ "name": "Ada" }

A dry-run executes the function to observe its result. Unlike SQL methods, a function's side effects are not rolled back.

Then fetch a generated TypeScript interface for all your functions:

GET https://api.centia.io/api/v4/function-interfaces HTTP/1.1
Accept: text/plain
Authorization: Bearer abc123
export interface Functions {
greet(event: { name: string }): Promise<{ greeting: string }>;
}

Functions without a dry-run fall back to Record<string, unknown> / unknown.

Get functions​

Get all functions
GET https://api.centia.io/api/v4/functions HTTP/1.1
Accept: application/json
Authorization: Bearer abc123
Get a specific function
GET https://api.centia.io/api/v4/functions/greet HTTP/1.1
Accept: application/json
Authorization: Bearer abc123

Update a function​

Changing the code bumps the function's version.

Update a function
PATCH https://api.centia.io/api/v4/functions/greet HTTP/1.1
Content-Type: application/json
Authorization: Bearer abc123

{
"code": "export const handler = async (event) => ({ greeting: `Hi, ${event.name}` });",
"timeout_s": 60
}

Delete a function​

Delete a function
DELETE https://api.centia.io/api/v4/functions/greet HTTP/1.1
Authorization: Bearer abc123

Status codes​

  • 201 Created — function created.
  • 200 OK — synchronous invocation completed (status is succeeded or failed).
  • 202 Accepted — asynchronous invocation queued.
  • 400 Bad Request — invalid definition (bad runtime, name, cron, event trigger or package).
  • 404 Not Found — no such function or invocation.
  • 501 Not Implemented — the function runtime is not configured on this instance.