Skip to content

Compute Functions

Overview

Compute Functions are serverless functions that run within Centrali's secure sandbox environment. They can be triggered by events, scheduled to run at specific times, or invoked on-demand.

What are Compute Functions?

Compute functions allow you to: - Process data: Transform, validate, or enrich records - Automate workflows: React to data changes automatically - Integrate systems: Call external APIs and services - Run scheduled tasks: Periodic data cleanup, reports, backups - Implement business logic: Custom calculations and validations

Key Features

Secure Sandbox

  • Functions run in isolated environments
  • No access to file system or network (except approved APIs)
  • CPU and memory limits enforced
  • Automatic timeout after 5 minutes

Built-in APIs

Functions have access to: - Data API: Query and modify records in your workspace - Storage API: Upload and download files - HTTP Client: Make external API calls - Crypto API: SHA256 hashing and HMAC-SHA256 signing - Base64 API: Encode and decode Base64 strings (useful for Basic Auth headers) - Logging: api.log() for general logging, api.logError() for error logging with context

Event-Driven

Functions can be triggered by: - Record events: Created, updated, deleted, restored, reverted - Schedule: Interval-based, cron expression, or one-time execution - Manual: On-demand invocation via API - HTTP Triggers: External HTTP requests

See Also: Event Payloads Reference for detailed documentation on event payload structures.

Creating a Function

Step 1: Create the Function

Functions are created in a single step with inline code:

curl -X POST https://api.centrali.io/workspace/my-workspace/api/v1/compute-functions \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "process-order",
    "description": "Process new orders and send notifications",
    "code": "async function run() {\n  const recordId = executionParams.recordId;\n  \n  // Query the order record\n  const order = await api.fetchRecord(recordId);\n  \n  // Call external API\n  await api.httpPost(\"https://api.example.com/notify\", {\n    orderId: order.id,\n    total: order.data.total\n  }, {\n    headers: { \"Authorization\": \"Bearer \" + triggerParams.apiKey }\n  });\n  \n  return { success: true };\n}"
  }'

Response:

{
  "id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
  "name": "process-order",
  "description": "Process new orders and send notifications",
  "code": "async function run() { ... }",
  "workspaceSlug": "my-workspace",
  "createdBy": "user-uuid",
  "createdAt": "2025-01-15T10:30:00.000Z",
  "updatedAt": "2025-01-15T10:30:00.000Z"
}

Step 2: Test the Function

Test function code before attaching triggers:

curl -X POST https://api.centrali.io/workspace/my-workspace/api/v1/compute-functions/test \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "code": "async function run() {\n  const recordId = executionParams.recordId;\n  const order = await api.fetchRecord(recordId);\n  return { success: true, orderId: order.id };\n}",
    "params": {
      "recordId": "550e8400-e29b-41d4-a716-446655440000"
    }
  }'

Step 3: Publish from Draft (Optional)

If you use the draft workflow, publish a draft to create the function:

curl -X POST https://api.centrali.io/workspace/my-workspace/api/v1/compute-functions/DRAFT_ID/publish \
  -H "Authorization: Bearer YOUR_TOKEN"

Function Code Structure

Basic Structure

All compute functions must define a run() function:

async function run() {
  // Your code here

  return {
    success: true,
    data: { /* your results */ }
  };
}

Important: The function must be named run, not handler or anything else.

Available Globals

Functions have access to these global objects:

Global Purpose
api Data operations, HTTP, utilities
triggerParams Static config from trigger setup (API keys, endpoints)
executionParams Runtime data passed when invoking the function
Date, Math Standard JavaScript globals
setTimeout, setInterval Timer functions

The api Object

// Record Operations
api.queryRecords(structureSlug, options)
api.fetchRecord(recordId)
api.fetchRecordByUniqueField(structureSlug, field, value)
api.createRecord(structureSlug, data)
api.updateRecord(recordId, data)
api.deleteRecord(recordId, hardDelete)
api.incrementField(recordId, field, amount)
api.decrementField(recordId, field, amount)
api.aggregateRecords(structureSlug, options)

// HTTP Client (domain allowlist required)
// Returns { status, statusText, headers, data }
api.httpGet(url, options)
api.httpPost(url, data, options)
api.httpPut(url, data, options)
api.httpDelete(url, options)

// Utilities
api.log(message)           // Logging (string or object)
api.uuid()                 // Generate UUID
api.formatDate(date, format)
api.renderTemplate(template, data, options)  // Handlebars templates

// Libraries
api.lodash                 // Subset of lodash functions
api.math                   // Math.js for calculations

Parameters

triggerParams - Static configuration set when creating the trigger:

// Configured in trigger setup
const apiKey = triggerParams.apiKey;
const endpoint = triggerParams.endpoint;

executionParams - Runtime data passed when invoking:

// For on-demand triggers: the payload from SDK/API call
// For event-driven triggers: the event payload (recordId, data, etc.)
const orderId = executionParams.orderId;
const action = executionParams.action;

Event-Driven Trigger Payload

For event-driven triggers, executionParams contains:

{
  event: "record_created",  // or record_updated, record_deleted, etc.
  workspaceSlug: "my-workspace",
  recordSlug: "orders",
  recordId: "uuid-here",
  data: { /* record data or { before, after } for updates */ },
  timestamp: "2025-01-15T10:00:00Z"
}

Function Examples

Example 1: Send Welcome Email

async function run() {
  // For event-driven triggers, executionParams contains the event
  const { recordId, data } = executionParams;

  // Send welcome email via external service
  await api.httpPost('https://api.sendgrid.com/v3/mail/send', {
    to: data.email,
    subject: 'Welcome!',
    text: `Hello ${data.name}, welcome to our platform!`
  }, {
    headers: {
      'Authorization': `Bearer ${triggerParams.SENDGRID_API_KEY}`
    }
  });

  api.log({ message: 'Welcome email sent', userId: recordId });

  return { success: true };
}

Example 2: Calculate Order Total

async function run() {
  const orderId = executionParams.recordId;

  // Get order record
  const order = await api.fetchRecord(orderId);

  // Query line items
  const lineItems = await api.queryRecords('order-items', {
    filter: { 'data.orderId': orderId }
  });

  // Calculate total
  let total = 0;
  for (const item of lineItems.data) {
    total += item.data.price * item.data.quantity;
  }

  // Update order with calculated total
  await api.updateRecord(orderId, { total });

  return { success: true, total };
}

Example 3: Scheduled Cleanup

async function run() {
  // Delete records older than 30 days
  const thirtyDaysAgo = new Date();
  thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);

  const oldRecords = await api.queryRecords('logs', {
    dateWindow: {
      field: 'createdAt',
      to: thirtyDaysAgo.toISOString()
    },
    pageSize: 100
  });

  let deleted = 0;
  for (const record of oldRecords.data) {
    await api.deleteRecord(record.id, true);
    deleted++;
  }

  api.log({ message: 'Cleanup complete', deleted });

  return { success: true, deleted };
}

Function Parameters

Static parameters are configured through trigger metadata (triggerMetadata.params) when creating or updating a trigger. They are available in your function code via the triggerParams global.

Configuring Parameters

Parameters are set in the trigger's triggerMetadata.params field:

curl -X POST https://api.centrali.io/workspace/my-workspace/api/v1/function-triggers \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "order-processor",
    "functionId": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
    "executionType": "on-demand",
    "triggerMetadata": {
      "params": {
        "WEBHOOK_URL": "https://api.example.com/webhook",
        "API_KEY": {
          "value": "sk_live_abc123",
          "encrypt": true
        }
      }
    }
  }'

Sensitive values can be encrypted at rest by using { "value": "...", "encrypt": true }. Encrypted parameters are automatically decrypted before function execution.

Accessing Parameters

Parameters are available in the triggerParams global:

async function run() {
  const apiKey = triggerParams.API_KEY;
  const webhookUrl = triggerParams.WEBHOOK_URL;

  // Use parameters in your code
  await api.httpPost(webhookUrl, { data: 'example' }, {
    headers: { 'Authorization': `Bearer ${apiKey}` }
  });

  return { success: true };
}

See the Function Triggers API for full documentation on parameter encryption.

Triggers

Triggers define when your function executes. Centrali supports four trigger types: event-driven (on record changes), scheduled (cron, interval, or one-time), on-demand (manual API/SDK invocation), and HTTP (external webhook URL).

For a complete guide on choosing and configuring triggers, see Triggers.

For the raw API endpoints, see the Function Triggers API Reference.

Monitoring & Debugging

View All Function Runs

curl -X GET https://api.centrali.io/workspace/my-workspace/api/v1/function-runs \
  -H "Authorization: Bearer YOUR_TOKEN"

View Runs for a Specific Function

curl -X GET https://api.centrali.io/workspace/my-workspace/api/v1/function-runs/function/f47ac10b-58cc-4372-a567-0e02b2c3d479 \
  -H "Authorization: Bearer YOUR_TOKEN"

View a Specific Run

curl -X GET https://api.centrali.io/workspace/my-workspace/api/v1/function-runs/RUN_ID \
  -H "Authorization: Bearer YOUR_TOKEN"

Re-run Functions

You can re-run any previous function execution to retry or debug. Re-run is supported for all trigger types (on-demand, event-driven, scheduled, and HTTP).

See Function Re-run documentation.

Best Practices

Code Organization

  • Keep functions focused on a single task
  • Extract reusable logic to helper functions
  • Use clear variable and function names
  • Add comments for complex logic

Error Handling

async function run() {
  try {
    // Your code here
    return { success: true };
  } catch (error) {
    // Log the error with context
    api.log({
      level: 'error',
      message: error.message,
      stack: error.stack
    });

    // Return error response (don't throw - return success: false)
    return { success: false, error: error.message };
  }
}

Performance

  • Minimize external API calls
  • Cache frequently accessed data
  • Use bulk operations for multiple records
  • Set appropriate timeout limits

Security

  • Store secrets as encrypted parameters
  • Validate input data
  • Use HTTPS for external calls
  • Follow principle of least privilege

Limits & Quotas

Default limits: - Execution time: 5 minutes maximum - Memory: 512 MB - Concurrent runs: 10 per function - Code size: 10 MB - External HTTP calls: 100 per execution

Next Steps

  1. Write your first function
  2. Set up triggers
  3. Learn the code API
  4. Configure webhooks