Expression Control
Expression controls provide a powerful JavaScript code editor that allows users to input executable code that is evaluated at runtime. This control is designed for advanced users who need dynamic data transformation, complex calculations, or conditional logic within their workflows.
Basic Usage
import * as i from '@xentom/integration-framework';
// Simple expression control
i.controls.expression({
label: 'Data Transform',
placeholder: 'Enter JavaScript expression...',
defaultValue: {
result: 'computed value',
},
});
// Multi-line code editor
i.controls.expression({
label: 'Custom Logic',
rows: 8,
placeholder: 'function transform(data) {\n return data;\n}',
});Configuration Options
Prop
Type
Important Note
Expression controls are only supported for node pins, not environment variables. This is because expressions need access to runtime data and workflow context that isn't available during environment configuration.
Runtime Context
Expression controls have access to runtime variables and data that flows through the workflow:
dataProcessor: i.nodes.callable({
inputs: {
sourceData: i.pins.data({
control: i.controls.text({
placeholder: 'Enter source data...',
}),
}),
transformExpression: i.pins.data({
control: i.controls.expression({
label: 'Transform Expression',
description: 'JavaScript code to transform the data',
rows: 6,
placeholder:
'// Transform the data\nreturn {\n processed: data.map(item => item.value * 2)\n};',
}),
}),
},
outputs: {
result: i.pins.data(),
},
run({ inputs, next }) {
// The expression has access to:
// - inputs from this node
// - variables from workflow context
// - previous node outputs
const result = evaluateExpression(inputs.transformExpression, {
data: inputs.sourceData,
// ... other context variables
});
next({ result });
},
});Common Use Cases
Data Transformation
transform: i.pins.data({
control: i.controls.expression({
label: 'Data Mapping',
description: 'Transform input data to desired output format',
rows: 8,
defaultValue: {
name: 'data.firstName + " " + data.lastName',
email: 'data.email.toLowerCase()',
createdAt: 'new Date().toISOString()',
},
}),
});Conditional Logic
condition: i.pins.data({
control: i.controls.expression({
label: 'Condition Check',
description: 'Boolean expression to determine flow path',
placeholder: 'user.age >= 18 && user.verified === true',
defaultValue: 'data.status === "active"',
}),
});Complex Calculations
calculator: i.pins.data({
control: i.controls.expression({
label: 'Price Calculation',
description: 'Calculate final price with taxes and discounts',
rows: 10,
defaultValue: `
// Calculate total with tax and discount
const subtotal = items.reduce((sum, item) => sum + (item.price * item.quantity), 0);
const discount = subtotal * (discountPercent / 100);
const taxableAmount = subtotal - discount;
const tax = taxableAmount * (taxRate / 100);
const total = taxableAmount + tax;
return {
subtotal,
discount,
tax,
total
};`,
}),
});API Response Processing
responseProcessor: i.pins.data({
control: i.controls.expression({
label: 'Response Processor',
description: 'Extract and format data from API response',
rows: 12,
defaultValue: `
// Process API response
if (!response.success) {
throw new Error('API request failed: ' + response.error);
}
return response.data.map(user => ({
id: user.id,
name: user.full_name,
email: user.email_address,
lastLogin: new Date(user.last_login_timestamp).toISOString(),
isActive: user.status === 'active'
}));`,
}),
});Built-in Functions and Variables
Expression controls have access to standard JavaScript functions plus workflow-specific variables:
// Available in expressions:
// - Standard JavaScript functions (Math, Date, JSON, etc.)
// - Workflow variables and data from connected nodes
// - Custom utility functions (if provided by the integration)
utilityExpression: i.pins.data({
control: i.controls.expression({
label: 'Utility Functions',
rows: 8,
defaultValue: `
// Standard JavaScript is available
const now = new Date();
const timestamp = Math.floor(now.getTime() / 1000);
// Access workflow data
const processedData = inputData.map(item => ({
...item,
processed: true,
timestamp: timestamp,
hash: btoa(JSON.stringify(item)) // Base64 encoding
}));
return processedData;`,
}),
});Error Handling
Expressions should include proper error handling:
safeProcessor: i.pins.data({
control: i.controls.expression({
label: 'Safe Data Processor',
description: 'Process data with error handling',
rows: 15,
defaultValue: `
try {
// Validate input
if (!data || !Array.isArray(data)) {
throw new Error('Input must be an array');
}
// Process each item
const results = data.map((item, index) => {
try {
return {
id: item.id || index,
value: parseFloat(item.value) || 0,
processed: true
};
} catch (itemError) {
console.warn('Error processing item', index, ':', itemError.message);
return {
id: index,
value: 0,
processed: false,
error: itemError.message
};
}
});
return results;
} catch (error) {
console.error('Processing failed:', error.message);
return { error: error.message };
}`,
}),
});Best Practices
Provide Clear Examples
// Good - Clear example with comments
filterExpression: i.pins.data({
control: i.controls.expression({
label: 'Filter Expression',
description: 'Filter array items based on criteria',
placeholder:
'// Filter active users over 18\ndata.filter(user => user.active && user.age > 18)',
defaultValue: 'data.filter(item => item.status === "active")',
}),
});Use Descriptive Names
// Good - Clear purpose
priceCalculation: i.pins.data({
control: i.controls.expression({
label: 'Price Calculation Logic',
description: 'Calculate final price including tax and discounts',
}),
});
// Avoid - Vague naming
expression: i.pins.data({
control: i.controls.expression({
label: 'Expression', // Too generic
}),
});Appropriate Row Count
Choose row count based on expected code complexity:
// Simple expressions - single line or few rows
simpleFilter: i.pins.data({
control: i.controls.expression({
rows: 2,
placeholder: 'item => item.active',
}),
});
// Complex logic - more rows
complexProcessor: i.pins.data({
control: i.controls.expression({
rows: 15,
placeholder:
'function processData(data) {\n // Complex processing logic\n}',
}),
});Provide Helpful Descriptions
validation: i.pins.data({
control: i.controls.expression({
label: 'Validation Rules',
description:
'Return true if data is valid, false otherwise. Available variables: data, user, config',
placeholder: 'data.email.includes("@") && data.age >= 13',
rows: 4,
}),
});Expression controls provide immense flexibility for advanced users while maintaining the visual workflow approach of Xentom. They bridge the gap between no-code simplicity and full programming power.
Select Control
Select controls provide a dropdown interface for choosing from a predefined set of options. They support both static option lists and dynamic options that are fetched asynchronously, making them ideal for configuration choices and API-driven selections.
Schema Validation
Schema validation in Xentom integrations provides runtime type safety and data validation using the Standard Schema specification. This ensures data integrity, provides clear error messages, and improves the developer experience with compile-time type inference.