Code Node - Custom JavaScript Execution
The Code node is one of the most powerful features in Circuitry, allowing you to write custom JavaScript code within your visual workflows. Perfect for data transformations, calculations, and custom logic that goes beyond standard nodes.
š Overview
The Code node bridges the gap between no-code and full programming flexibility. While Circuitry is designed to be no-code first, sometimes you need that extra bit of custom logic - and that's where the Code node shines.
Key Benefits
- Full JavaScript Support: Write any valid JavaScript code
- Access to Input Data: Full access to data from previous nodes
- Template Variables: Use
{{variables}}
within your code - No External Dependencies: Runs securely in an isolated environment
- Instant Testing: Test your code with sample data before executing
š Basic Usage
Simple Example
// Access input data from previous node
const message = input.message;
const timestamp = new Date().toISOString();
// Return processed data
return {
original: message,
processed: message.toUpperCase(),
timestamp: timestamp,
wordCount: message.split(' ').length
};
Input and Output
Input: The Code node receives data from the previous node as the input
variable
Output: Whatever you return
becomes the output for the next node
šÆ Common Use Cases
1. Data Transformation
Transform data structures to match your needs:
// Transform array of objects
const users = input.users || [];
const transformed = users.map(user => ({
fullName: `${user.firstName} ${user.lastName}`,
email: user.email.toLowerCase(),
age: calculateAge(user.birthDate),
status: user.isActive ? 'active' : 'inactive'
}));
function calculateAge(birthDate) {
const diff = Date.now() - new Date(birthDate).getTime();
return Math.floor(diff / (1000 * 60 * 60 * 24 * 365.25));
}
return { users: transformed };
2. Data Validation
Validate and clean incoming data:
// Validate email and phone
const email = input.email;
const phone = input.phone;
const emailRegex = /^[^s@]+@[^s@]+.[^s@]+$/;
const phoneRegex = /^d{10}$/;
const errors = [];
if (!emailRegex.test(email)) {
errors.push('Invalid email format');
}
if (!phoneRegex.test(phone.replace(/D/g, ''))) {
errors.push('Invalid phone number');
}
return {
isValid: errors.length === 0,
errors: errors,
cleaned: {
email: email.trim().toLowerCase(),
phone: phone.replace(/D/g, '')
}
};
3. Complex Calculations
Perform calculations that would be difficult with standard nodes:
// Calculate order totals with tax and discounts
const items = input.orderItems || [];
const taxRate = input.taxRate || 0.08;
const discountCode = input.discountCode;
let subtotal = 0;
let discountAmount = 0;
// Calculate subtotal
items.forEach(item => {
subtotal += item.price * item.quantity;
});
// Apply discount
if (discountCode === 'SAVE10') {
discountAmount = subtotal * 0.10;
} else if (discountCode === 'SAVE20') {
discountAmount = subtotal * 0.20;
}
const afterDiscount = subtotal - discountAmount;
const tax = afterDiscount * taxRate;
const total = afterDiscount + tax;
return {
subtotal: subtotal.toFixed(2),
discount: discountAmount.toFixed(2),
tax: tax.toFixed(2),
total: total.toFixed(2),
itemCount: items.reduce((sum, item) => sum + item.quantity, 0)
};
4. Data Aggregation
Aggregate and summarize data:
// Analyze sales data
const sales = input.sales || [];
const summary = {
totalSales: 0,
averageSale: 0,
topProduct: null,
byCategory: {},
byMonth: {}
};
const productCounts = {};
sales.forEach(sale => {
// Total sales
summary.totalSales += sale.amount;
// By category
const category = sale.category;
if (!summary.byCategory[category]) {
summary.byCategory[category] = 0;
}
summary.byCategory[category] += sale.amount;
// By month
const month = new Date(sale.date).toLocaleString('default', { month: 'long' });
if (!summary.byMonth[month]) {
summary.byMonth[month] = 0;
}
summary.byMonth[month] += sale.amount;
// Product counts
const product = sale.product;
productCounts[product] = (productCounts[product] || 0) + 1;
});
// Calculate average
summary.averageSale = (summary.totalSales / sales.length).toFixed(2);
// Find top product
const topProductName = Object.keys(productCounts).reduce((a, b) =>
productCounts[a] > productCounts[b] ? a : b
);
summary.topProduct = {
name: topProductName,
count: productCounts[topProductName]
};
return summary;
5. API Response Processing
Process and extract data from API responses:
// Process weather API response
const weatherData = input.apiResponse;
if (!weatherData || weatherData.error) {
return {
error: true,
message: weatherData?.error || 'No data received'
};
}
const current = weatherData.current;
const forecast = weatherData.forecast?.forecastday || [];
// Extract relevant information
const processed = {
location: `${weatherData.location.name}, ${weatherData.location.country}`,
current: {
temp: `${current.temp_f}°F`,
condition: current.condition.text,
humidity: `${current.humidity}%`,
windSpeed: `${current.wind_mph} mph`
},
forecast: forecast.slice(0, 3).map(day => ({
date: day.date,
high: `${day.day.maxtemp_f}°F`,
low: `${day.day.mintemp_f}°F`,
condition: day.day.condition.text
})),
alerts: weatherData.alerts?.length > 0
};
return processed;
š§ Advanced Features
Using Template Variables
Combine template variables with JavaScript:
// Template variables are replaced before code execution
const userName = "{{user.name}}";
const apiKey = "{{env.API_KEY}}";
const previousResult = "{{nodes.agent1.output}}";
// Use them in your logic
return {
greeting: `Hello, ${userName}!`,
authorized: apiKey !== 'undefined',
enhanced: processData(previousResult)
};
function processData(data) {
// Your processing logic
return data.toUpperCase();
}
Working with Dates
Common date operations:
// Date utilities
const now = new Date();
const inputDate = new Date(input.date);
// Format dates
const formatted = {
iso: now.toISOString(),
local: now.toLocaleString(),
dateOnly: now.toLocaleDateString(),
timeOnly: now.toLocaleTimeString()
};
// Calculate differences
const daysDiff = Math.floor((now - inputDate) / (1000 * 60 * 60 * 24));
const isOverdue = inputDate < now;
// Add/subtract days
const futureDate = new Date(now);
futureDate.setDate(futureDate.getDate() + 30);
return {
current: formatted,
daysSince: daysDiff,
isOverdue: isOverdue,
dueIn30Days: futureDate.toISOString()
};
Array Operations
Powerful array manipulations:
// Advanced array operations
const items = input.items || [];
// Filter, map, reduce
const processed = items
.filter(item => item.active)
.map(item => ({
...item,
value: item.price * item.quantity
}))
.sort((a, b) => b.value - a.value);
// Group by category
const grouped = items.reduce((acc, item) => {
const key = item.category;
if (!acc[key]) acc[key] = [];
acc[key].push(item);
return acc;
}, {});
// Find duplicates
const seen = new Set();
const duplicates = items.filter(item => {
const duplicate = seen.has(item.id);
seen.add(item.id);
return duplicate;
});
return {
processed: processed,
grouped: grouped,
duplicates: duplicates,
stats: {
total: items.length,
active: items.filter(i => i.active).length,
categories: Object.keys(grouped).length
}
};
Error Handling
Robust error handling:
try {
// Potentially risky operation
const data = JSON.parse(input.jsonString);
// Validate required fields
const required = ['name', 'email', 'age'];
const missing = required.filter(field => !data[field]);
if (missing.length > 0) {
throw new Error(`Missing required fields: ${missing.join(', ')}`);
}
// Process data
const result = processUserData(data);
return {
success: true,
data: result
};
} catch (error) {
return {
success: false,
error: error.message,
input: input
};
}
function processUserData(data) {
// Your processing logic
return {
...data,
processed: true,
timestamp: Date.now()
};
}
š Best Practices
1. Always Return Data
Every Code node should return something:
// Good ā
return {
result: processedData,
status: 'complete'
};
// Bad ā
processData(); // No return statement
2. Handle Missing Input
Check for undefined or null values:
// Safe input handling
const items = input.items || [];
const config = input.config || {};
const name = input.name || 'Unknown';
// Check before accessing nested properties
const city = input.address?.city || 'N/A';
3. Use Meaningful Variable Names
// Good ā
const userEmail = input.email;
const orderTotal = calculateTotal(items);
// Bad ā
const e = input.email;
const t = calc(i);
4. Comment Complex Logic
// Calculate compound interest
// Formula: A = P(1 + r/n)^(nt)
const principal = input.principal;
const rate = input.annualRate / 100;
const time = input.years;
const n = 12; // Monthly compounding
const amount = principal * Math.pow(1 + rate/n, n * time);
5. Test with Edge Cases
Consider:
- Empty arrays
- Null/undefined values
- Invalid data types
- Zero/negative numbers
- Empty strings
š Security Considerations
Safe Practices
The Code node runs in a sandboxed environment with these limitations:
- No file system access
- No network requests (use Action nodes for HTTP)
- No external modules (no require/import)
- No global scope pollution
- Execution timeout (prevents infinite loops)
What You CAN Do
ā All standard JavaScript features ā JSON operations ā Date/time manipulation ā Math calculations ā String/array operations ā Regular expressions ā Object manipulation
What You CANNOT Do
ā File operations ā Direct HTTP requests ā Import external libraries ā Access browser APIs ā Modify global objects
š” Tips and Tricks
1. Debugging
Use console.log for debugging (visible in browser console):
console.log('Input data:', input);
console.log('Processing step 1...');
const result = processData(input);
console.log('Result:', result);
return result;
2. Type Checking
Validate data types:
// Type checking utilities
function isNumber(val) {
return typeof val === 'number' && !isNaN(val);
}
function isArray(val) {
return Array.isArray(val);
}
function isObject(val) {
return val !== null && typeof val === 'object' && !Array.isArray(val);
}
3. Default Values
Use default parameters and nullish coalescing:
// Default values
const process = (data = {}, options = {}) => {
const limit = options.limit ?? 10;
const offset = options.offset ?? 0;
// ...
};
š Examples Gallery
Generate UUID
function generateUUID() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
const r = Math.random() * 16 | 0;
const v = c === 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
return {
id: generateUUID(),
timestamp: Date.now(),
...input
};
Parse CSV Data
const csvText = input.csvData;
const lines = csvText.split('\n');
const headers = lines[0].split(',');
const data = lines.slice(1).map(line => {
const values = line.split(',');
return headers.reduce((obj, header, index) => {
obj[header.trim()] = values[index]?.trim() || '';
return obj;
}, {});
});
return {
headers: headers,
rows: data,
count: data.length
};
Generate Statistics
const numbers = input.values || [];
const stats = {
count: numbers.length,
sum: numbers.reduce((a, b) => a + b, 0),
mean: 0,
median: 0,
min: Math.min(...numbers),
max: Math.max(...numbers)
};
stats.mean = stats.sum / stats.count;
const sorted = [...numbers].sort((a, b) => a - b);
const mid = Math.floor(sorted.length / 2);
stats.median = sorted.length % 2 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2;
return stats;
Next Steps
- Template Variables Guide - Learn about dynamic variable replacement
- Workflow Examples - See Code nodes in action
- API Integration - Combine Code nodes with API calls