Trigger Functions
Trigger functions execute automatically when data changes in a collection. Use them for validation, transformation, notifications, and integrations.
How It Works
- Configure which collection to monitor
- Select which operations trigger execution (POST, PUT, PATCH, DELETE)
- Write JavaScript code or configure an external webhook
- Function runs automatically on matching operations
Configuration
Basic Settings
| Setting | Description |
|---|---|
| Name | Unique identifier for the function |
| Description | Optional documentation |
| Enabled | Toggle to enable/disable without deleting |
| Collection | The collection to monitor |
| Methods | Which operations trigger execution |
Triggering Methods
| Method | When It Fires |
|---|---|
POST | New item created |
PUT | Item replaced |
PATCH | Item updated |
DELETE | Item deleted |
Select one or more methods. The function fires for each matching operation.
Execution Context
What Your Code Receives
| Object | Description |
|---|---|
item | The record being created, updated, or deleted |
me | The user who triggered the change |
req | Request details (method, headers, query) |
api | API information |
secrets | Configured secrets |
collectionName | Name of the collection being processed |
validationErrors | Array for validation errors (returns 400 if non-empty) |
Token Options
Control whose permissions the function uses:
| Option | Description |
|---|---|
| None | No authentication (anonymous) |
| Current User | Execute as the user who triggered the change |
| Service Account | Execute with service account permissions |
Execution Control
| Option | Description |
|---|---|
| Skip Security Policy | Bypass row-level access control |
| Skip Roles | Ignore role-based permissions |
| Halt on Error | Stop the operation if the function crashes unexpectedly |
Understanding Halt on Error
Halt on Error controls what happens when your function crashes due to unexpected errors (runtime errors, failed external requests, etc.) — it does NOT affect validation errors.
| Scenario | Halt on Error ON | Halt on Error OFF |
|---|---|---|
Validation errors (validationErrors) | Operation blocked | Operation blocked |
| Runtime error (e.g., undefined variable) | Operation blocked | Operation continues |
| External fetch fails | Operation blocked | Operation continues |
Example: You have a trigger that validates input AND syncs to an external CRM:
// Validation - always blocks if invalid
if (!item.email) {
validationErrors.push('Email is required');
}
// External sync - might fail
await fetch('https://crm.example.com/sync', { ... });
- With Halt on Error ON: If CRM is down, the insert is blocked
- With Halt on Error OFF: If CRM is down, the insert proceeds anyway (validation still works)
Use Cases
Data Validation
Use the validationErrors array to reject invalid data. If this array has any items when the function completes, the operation returns 400 Bad Request with the errors in the response body.
// Reject orders with invalid totals
if (item.total < 0) {
validationErrors.push('Order total cannot be negative');
}
if (!item.items || item.items.length === 0) {
validationErrors.push('Order must have at least one item');
}
if (item.total > 10000 && !item.approvedBy) {
validationErrors.push('Orders over $10,000 require approval');
}
Multiple validation errors can be collected and returned together:
["Order total cannot be negative", "Order must have at least one item"]
- Validation errors (
validationErrors.push(...)) — Returned to the client in the response body - Runtime errors (undefined variables, failed fetch, etc.) — Only logged server-side, not exposed to client
Data Transformation
Transform values before saving:
// Auto-generate slug from title
item.slug = item.title
.toLowerCase()
.replace(/[^a-z0-9]+/g, '-')
.replace(/^-|-$/g, '');
// Set timestamps
item.updatedAt = new Date().toISOString();
item.updatedBy = me.id;
Notifications
Send notifications on changes (requires paid tier):
// Notify when order status changes
if (item.status === 'shipped') {
await fetch('https://api.email-service.com/send', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
to: item.customerEmail,
subject: 'Your order has shipped!',
body: `Order ${item.orderNumber} is on its way.`
})
});
}
External Sync
Sync data with external systems (requires paid tier):
// Sync customer to CRM
await fetch('https://crm.example.com/api/contacts', {
method: 'POST',
headers: {
'Authorization': `Bearer ${secrets.CRM_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
email: item.email,
name: item.name,
source: 'website'
})
});
Cascade Operations
Perform related operations using built-in methods:
// When a project is deleted, archive its tasks
if (req.method === 'DELETE') {
const tasksRes = await get(`tasks?filter=projectId eq "${item.id}"`);
for (const task of tasksRes.data) {
await patch(`tasks/${task.id}`, { archived: true });
}
}
External Endpoint Action
Instead of JavaScript, call an external webhook:
| Setting | Description |
|---|---|
| URL | The webhook endpoint to call |
| Custom Headers | Additional headers to include |
| Send Token | Include authentication token |
| Halt on Error | Stop if webhook fails |
The webhook receives the same context as JavaScript functions.
Best Practices
- Keep functions focused — One function, one responsibility
- Use
validationErrorsfor user-facing errors — These are returned in the response - Use built-in methods —
get(),post(),patch(),del()for internal API calls - Use Halt on Error carefully — Enable when external failures should block the operation
- Test thoroughly — Use the built-in test runner before enabling
- Log important events — Use
log()for debugging and auditing