Plexivo Functions

Run your own TypeScript next to your database, called over HTTPS, on a schedule, when rows change or when a user signs up.

Plexivo Functions run your own server-side code beside your Plexivo Postgres: taking payments, sending emails, calling other APIs, and anything else that needs a secret. Each function knows who called it and can query your database as them, so row-level security still applies.

Set up the runtime#

Functions run in a Plexivo App Runner service of your own, isolated and asleep when idle, and billed as that service. You set it up once per database:

  1. Turn on the Data API on the Connect tab. Functions use the same project endpoint and keys.
  2. Open the Functions tab and copy the Plexivo Functions image shown under Set up the runtime.
  3. Create a public Plexivo App Runner service from that image, on port 8000. Plexivo gives it its HTTPS address and certificate.
  4. Choose it as the Service and choose Connect runtime.

Write a function#

Choose New function, give it a Name such as send-receipt, write it, and choose Deploy. A function is a module whose default export takes the request and a context:

ts
export default async (req: Request, ctx) => {
  const { orderId } = await req.json();

  // As the caller: row-level security applies
  const { data: order } = await ctx.plexivo.from('orders')
    .select('id, total').eq('id', orderId).single();

  await fetch('https://api.example.com/receipts', {
    method: 'POST',
    headers: { authorization: `Bearer ${ctx.env.RECEIPTS_API_KEY}` },
    body: JSON.stringify({ email: ctx.user?.email, total: order.total })
  });

  ctx.log.info('receipt sent for', orderId);
  return { sent: true };
};
In ctxWhat it is
user, roleThe caller’s verified claims, or null for a visitor; anon, authenticated or service_role
plexivoA Plexivo JavaScript client acting as the caller: from, select, eq, insert, update, rpc
adminA Plexivo client with the service role, past row-level security
envThe function’s secrets
loginfo, warn, error and debug, kept with each run
trigger, eventWhat started the run, and the row or user for an event

Return a Response, or any value to send it as JSON. Each deploy is a new version; Versions lets you Roll back to this any earlier one.

Call a function#

js
const { data } = await db.functions.invoke('send-receipt', { body: { orderId } });
dart
final res = await db.functions.invoke('send-receipt', body: {'orderId': orderId});
print(res.data);

Over HTTPS, a function is at your Project URL followed by /functions/v1/send-receipt, with any method and sub-path. By default, Only signed-in users (and servers) may call it over HTTP is on: visitors who have not signed in are refused, and servers call it with the secret key.

Triggers#

Schedule (cron, UTC)
Five-field cron in UTC, such as */15 * * * *, or @hourly, @daily, @weekly, @monthly and @yearly. Scheduled runs act as the service role. 03:30 UTC is 09:00 in India.
Run when rows change
Pick a table and insert, update or delete, up to 20 entries. ctx.event holds the new row as record and, for updates and deletes, the previous row as old_record.
Run when a user signs up
Runs for each new user, for a welcome email or a profile row.

Row changes and sign-ups are recorded in the same transaction as the change, so none is lost. Each is delivered at least once and retried with growing pauses, up to 8 attempts, until the function answers with success, so make your function safe to run twice for the same event_id.

Secrets#

Add secrets under Function secrets: a NAME in capitals and its value. Every function reads them from ctx.env, and values are never shown again. Names starting with PLEXIVO_ are reserved. Up to 100 secrets, each up to 8 KB.

Test and watch#

Test runs the function for real, As a visitor or As the service role, and shows its response and logs. Recent runs lists every run with its status and time; logs are kept for 7 days.

Limits#

  • Time limit (ms): 10 seconds by default, up to 60 seconds.
  • Request body: up to 6 MB.
  • Code: up to 512 KB per function. Names are lowercase letters, digits and dashes, starting with a letter.
  • Each run is isolated: it can reach the network, but not the file system or other processes.
For startups Payments and emails do not need a separate server: a function with your payment provider’s secret in ctx.env, called from your app and run again by a row change, is the whole backend for a checkout.

Next steps#

Updated 27 October 2026