Skip to main content

JavaScript Environment

Functions execute JavaScript in serverless containers. This page covers the execution environment, available objects, and best practices.

Available Objects

Core Objects

ObjectDescriptionAvailable In
itemThe current record being created, updated, or deletedTrigger functions
meCurrent authenticated userAll functions (when authenticated)
apiInformation about the APIAll functions
secretsConfigured secrets (key/value pairs)All functions
reqIncoming HTTP request detailsTrigger & Invocable functions
resResponse object for custom responsesInvocable functions
collectionNameName of the collection being processedTrigger functions
validationErrorsArray for validation errors (returns 400 if non-empty)All functions

The item Object

For trigger functions, item contains the record being processed:

// Access item properties
log('Processing:', item.name);
log('Item ID:', item.id);

// Modify the item (for validation/transformation)
item.slug = item.title.toLowerCase().replace(/\s+/g, '-');
item.updatedAt = new Date().toISOString();

The me Object

Information about the current user:

PropertyDescription
idUser ID (GUID)
emailUser email
nameUser display name
roleIdsArray of role IDs assigned to the user
log('Request by:', me.name);
log('User email:', me.email);
log('User roles:', me.roleIds);

The api Object

Information about the current API:

PropertyDescription
idAPI ID (GUID)
nameAPI name
tenantIdTenant ID (GUID)
log('Running in API:', api.name);
log('Tenant:', api.tenantId);

The req Object

Request details (trigger and invocable functions):

PropertyDescription
methodHTTP method (GET, POST, PUT, PATCH, DELETE)
headersRequest headers (filtered for security)
queryQuery parameters
bodyRaw body string
bodyJsonParsed JSON body
// Access request data
const method = req.method;
const contentType = req.headers['content-type'];
const page = req.query.page || 1;
const payload = req.bodyJson;

The res Object (Invocable Only)

Set custom responses in invocable functions:

PropertyDescription
statusHTTP status code (default: 200)
headersResponse headers
bodyJsonJSON response body
bodyPlain text response body
// Return custom response
res = {
status: 201,
headers: { 'X-Custom-Header': 'value' },
bodyJson: { success: true, id: newItem.id }
};

Real-Time Channels

Functions can manage private channels (channels with : in the name) using the built-in HTTP methods:

// Add client to a private channel with send permission
await post('/_rt/join', {
connectionId: req.bodyJson.connectionId,
channel: 'room:123',
readOnly: false // Client can send messages
});

// Add client as read-only listener
await post('/_rt/join', {
connectionId: req.bodyJson.connectionId,
channel: 'announcements:system',
readOnly: true // Client can only receive
});

// Send message to any channel
await post('/_rt/send', {
channel: 'announcements:system',
body: { type: 'notification', text: 'New update available' }
});

Private channels (with :) can only be joined/messaged by functions. The readOnly parameter controls whether the client can send messages after joining.

See Real Time for channel types and client-side usage.

Built-in HTTP Methods

Functions have built-in async methods for API calls:

MethodDescription
get(url)GET request to API endpoint
post(url, data)POST request
put(url, data)PUT request
patch(url, data)PATCH request
del(url)DELETE request
// Get all active products
const products = await get('products?filter=status eq "active"');

// Create a new order
const order = await post('orders', {
customerId: item.customerId,
total: item.total
});

// Update a record
await patch(`customers/${item.customerId}`, {
lastOrderDate: new Date().toISOString()
});

// Delete a record
await del(`temp-items/${item.id}`);

These methods automatically include authentication and use relative URLs within your API.

User-Context Operations

By default, the built-in HTTP methods (get, post, etc.) run with full admin privileges, bypassing access rules. Use user.* methods to run operations as the current user, respecting their access permissions:

MethodDescription
user.get(url)GET as the current user
user.post(url, data)POST as the current user
user.put(url, data)PUT as the current user
user.patch(url, data)PATCH as the current user
user.delete(url)DELETE as the current user
user.getFileBase64(url)Get file as user
user.postFileBase64(...)Upload file as user
// Admin context - bypasses access rules
const allOrders = await get('orders');

// User context - only returns orders the user can access
const userOrders = await user.get('orders');

// User context - will fail if user lacks write access
await user.post('products', { name: 'New Product' });

This is useful when:

  • You want to enforce access rules within function logic
  • You need to verify what the user can actually see/modify
  • Building multi-tenant logic where users should only see their own data

External HTTP Requests (fetch)

Use fetch for external API calls:

const response = await fetch('https://api.example.com/data', {
method: 'POST',
headers: {
'Authorization': `Bearer ${secrets.EXTERNAL_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ key: 'value' })
});

const data = await response.json();
Paid Feature

The fetch function for external HTTP requests is only available on paid tiers. Free tier APIs cannot make external HTTP calls.

File Operations

MethodDescription
getFileBase64(url)Get file as Base64 string
postFileBase64(url, fileName, content, mimeType)Upload Base64 file
// Download a file
const fileContent = await getFileBase64(`products/${item.id}/image`);

// Upload a file
await postFileBase64(
`products/${item.id}/thumbnail`,
'thumbnail.jpg',
resizedImageBase64,
'image/jpeg'
);

Sequences

Use getSequence() to generate atomic, auto-incrementing numbers. Each call returns the next value in the sequence, guaranteed to be unique even under concurrent execution.

const orderNumber = await getSequence();
item.orderNumber = orderNumber; // e.g., 1, 2, 3, ...

This is useful for:

  • Order numbers — Human-readable sequential IDs
  • Invoice numbers — Sequential numbering for accounting
  • Ticket numbers — Support ticket or queue numbers
  • Any sequential identifier — When UUIDs aren't appropriate
Atomic Guarantees

getSequence() uses database-level sequencing, ensuring no duplicates even when multiple function instances run concurrently.

Secrets

Secrets are securely stored credentials injected into your function.

Configuring Secrets

  1. Create secrets in SettingsSecrets
  2. Select which secrets to inject when configuring your function
  3. Access them via the secrets object

Using Secrets

// Access secrets
const apiKey = secrets.STRIPE_API_KEY;
const webhookSecret = secrets.WEBHOOK_SECRET;

// Use in external calls
const response = await fetch('https://api.stripe.com/v1/charges', {
headers: {
'Authorization': `Bearer ${secrets.STRIPE_SECRET_KEY}`
}
});
warning

Never log secrets or include them in error messages.

Logging

FunctionSeverity
log(...args)Info (multiple arguments supported)
logInfo(message)Info
logWarn(message)Warning
logError(message)Error
log('Processing started');
log('Item:', item.name, 'Status:', item.status); // Multiple args
logInfo('Single info message');
logWarn('Customer has pending balance');
logError('Failed to sync with external service');

Logs include timestamps and milliseconds from execution start.

Context by Function Type

Trigger Functions

// Available: item, me, api, secrets, req, collectionName, validationErrors
log('Processing item:', item.id);
log('Collection:', collectionName);
log('Triggered by:', me.name);
log('Method:', req.method);

// Validate
if (!item.title) {
validationErrors.push('Title is required');
}

// Modify item before save
item.updatedBy = me.id;

Timer Functions

// Available: api, me (service account), secrets
// No item or request context
log('Running scheduled task for API:', api.name);

const ordersRes = await get('orders?filter=status eq "pending"');
for (const order of ordersRes.data) {
// Process each order...
}

Invocable Functions

// Available: req, res, me, api, secrets
const input = req.bodyJson;
const result = processData(input);

res = {
status: 200,
bodyJson: { success: true, result }
};

Error Handling

Validation Errors

Use validationErrors to return user-facing errors. If this array has any items when the function completes, the operation returns 400 Bad Request with the errors in the response body.

if (!item.email) {
validationErrors.push('Email is required');
}

if (item.price < 0) {
validationErrors.push('Price cannot be negative');
}

// Multiple errors are returned together

Response when validation fails:

["Email is required", "Price cannot be negative"]

Runtime Errors vs Validation Errors

TypeExampleReturned to ClientLogged
Validation errorsvalidationErrors.push('...')Yes (400 response)Yes
Runtime errorsundefined.foo, failed fetchNoYes

Runtime errors (like undefined variables or failed external requests) are only logged server-side and not exposed to the client. Use validationErrors for errors you want users to see.

Try-Catch

Handle external failures gracefully:

try {
const external = await fetch(externalUrl);
if (!external.ok) {
logError('External API returned: ' + external.status);
validationErrors.push('Unable to verify with external service');
}
} catch (error) {
logError('External call failed: ' + error.message);
// Decide: add to validationErrors or let it proceed?
}

Filtered Headers

For security, these headers are removed from req.headers:

  • authorization, cookie, host
  • x-forwarded-*, x-azure-*, x-arr-*
  • Other infrastructure headers

Best Practices

  • Use item not body — For trigger functions, the record is in item
  • Use built-in methodsget(), post(), etc. handle auth automatically
  • Use validationErrors for user-facing errors — These are returned in the response
  • Handle external failures with try-catch — Decide whether to block or continue
  • Log important operations — Aids debugging and auditing
  • Keep secrets secure — Never expose in logs or responses