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