Run Script
Execute custom scripts as part of NocoDB workflows.
The Run Script action node in NocoDB workflows allows you to execute custom JavaScript logic as part of an automated workflow. This node is useful when built-in actions are not sufficient, and you need fine-grained control over tables, fields, records, or workflow data.

Configuration
Inputs
- Define input variables that can be accessed inside the script using
input.config(). - Inputs can be static values or mapped from outputs of previous workflow nodes.
Script
- Write JavaScript code to perform custom logic.
- Scripts can read workflow inputs, interact with tables, and perform conditional logic.
- Use Edit code to open the script editor.
Test Step
- Use Test this action to execute the script with the configured inputs.
- Test results and logs are displayed in the right panel.
Script Editor
Use the script editor to write and test your JavaScript code. The editor provides syntax highlighting and error checking to help you develop your script. Debugging information is available on the panel to the right.

Available APIs
Workflow scripts have access to a subset of the NocoDB Scripting APIs. Since workflows run in an automated context without user interaction, some APIs are not available.
Input API
Access input variables configured in the workflow node:
// Get all input values as an object
const config = input.config();
// Access specific input values
const recordId = input.config().recordId;
const tableName = input.config().tableName;
const threshold = input.config().threshold;Output API
Set output values that can be used by downstream workflow nodes:
// Set a single output value
output.set('processedCount', 42);
output.set('status', 'success');
output.set('results', { updated: 10, skipped: 5 });
// Output values are available to subsequent nodes in the workflowOther APIs
The following APIs work the same as in interactive scripts. Refer to the NocoDB Scripting Guide for full documentation:
- Base & Table operations - Full CRUD operations on tables, views, fields, and records
- HTTP fetch - Make external API calls
- Console logging -
console.log(),console.warn(),console.error()for debugging - Collaborators - Access base collaborators via
base.activeCollaborators - UITypes - Field type constants for type checking
APIs Not Available in Workflows
The following APIs from interactive scripts are not available in workflow scripts:
| API | Reason |
|---|---|
input.textAsync(), input.buttonsAsync(), input.selectAsync(), etc. | Workflows run without user interaction |
input.tableAsync(), input.viewAsync(), input.fieldAsync(), input.recordAsync() | Interactive selection not available |
input.fileAsync() | File uploads not available in automated context |
output.text(), output.markdown(), output.table(), output.inspect() | No UI display in workflows |
output.clear() | No UI to clear |
script.step(), script.clear() | Script steps are for interactive feedback |
session.currentUser | No user session in automated workflows |
cursor | No UI context for cursor position |
input.config({...}) with form builder | Use workflow node inputs instead |
How it Works
- When the workflow reaches the Run Script node, NocoDB initializes a sandbox execution environment.
- Input variables configured in the node are made available via
input.config(). - The script executes with full access to the base's tables and records.
- Output values set via
output.set()become available to downstream nodes. - Once execution completes, the workflow proceeds to the next node.
Examples
Process Records Based on Criteria
const config = input.config();
const table = base.getTable(config.tableName);
// Find records that need processing
const records = await table.selectRecordsAsync({
where: '(Status,eq,Pending)',
fields: ['Title', 'Status', 'Priority']
});
let processedCount = 0;
for (const record of records.records) {
const priority = record.getCellValue('Priority');
if (priority === 'High') {
await table.updateRecordAsync(record.id, {
'Status': 'In Progress'
});
processedCount++;
}
}
output.set('processedCount', processedCount);
output.set('totalFound', records.records.length);Call External API and Update Records
const config = input.config();
const recordId = config.recordId;
const table = base.getTable('Orders');
const record = await table.selectRecordAsync(recordId);
const orderId = record.getCellValue('External Order ID');
// Call external API
const response = await fetch(`https://api.shipping.com/status/${orderId}`, {
headers: { 'Authorization': `Bearer ${config.apiKey}` }
});
if (response.status === 200) {
const shippingStatus = response.data.status;
await table.updateRecordAsync(recordId, {
'Shipping Status': shippingStatus,
'Last Updated': new Date().toISOString()
});
output.set('status', 'success');
output.set('shippingStatus', shippingStatus);
} else {
output.set('status', 'error');
output.set('errorMessage', response.statusText);
}Aggregate Data Across Tables
const ordersTable = base.getTable('Orders');
const customersTable = base.getTable('Customers');
const customerId = input.config().customerId;
// Get all orders for a customer
const orders = await ordersTable.selectRecordsAsync({
where: `(Customer ID,eq,${customerId})`,
fields: ['Order Total', 'Order Date', 'Status']
});
// Calculate totals
let totalSpent = 0;
let orderCount = 0;
for (const order of orders.records) {
if (order.getCellValue('Status') === 'Completed') {
totalSpent += order.getCellValue('Order Total') || 0;
orderCount++;
}
}
// Update customer record
await customersTable.updateRecordAsync(customerId, {
'Total Spent': totalSpent,
'Order Count': orderCount,
'Last Calculated': new Date().toISOString()
});
output.set('totalSpent', totalSpent);
output.set('orderCount', orderCount);Create Records in Bulk
const config = input.config();
const sourceTable = base.getTable(config.sourceTable);
const targetTable = base.getTable(config.targetTable);
const records = await sourceTable.selectRecordsAsync({
where: '(Processed,eq,false)',
pageSize: 10
});
const createdIds = [];
for (const record of records.records) {
const newId = await targetTable.createRecordAsync({
'Title': record.getCellValue('Title'),
'Description': record.getCellValue('Description'),
'Source Record ID': record.id
});
createdIds.push(newId);
// Mark as processed
await sourceTable.updateRecordAsync(record.id, {
'Processed': true
});
}
output.set('createdCount', createdIds.length);
output.set('createdIds', createdIds);Use Cases
- Perform complex data transformations or calculations that are not supported by standard nodes.
- Integrate with external APIs or services.
- Implement custom validation or business logic.
- Create or modify records dynamically based on complex conditions.
- Aggregate data across multiple tables.
- Generate computed values for downstream workflow nodes.
Related Resources
- NocoDB Scripting Guide - Full scripting documentation (note: some features are not available in workflows)
- Workflow Overview
- Action Nodes