n8nen.nl logo n8nen.nl

N8N Code Node: JavaScript & Python voor Custom Logic in je Workflows

2025-01-29 Sam Stroobandt
N8N Code Node: JavaScript & Python voor Custom Logic in je Workflows
JavaScriptFast & NativePythonML & Data ScienceCode NodeTransform • Process • Integrate$json • $input • return itemsData OutputTransformed ResultsAPI CallsExternal ServicesComplex LogicBusiness RulesChoose Your LanguageWrite Custom LogicProcess & Transform

Waarom de Code Node Game-Changing is

Stel je voor: je hebt een perfecte workflow, maar je loopt tegen een limitatie aan. Misschien moet je complexe data transformeren, een custom algoritme implementeren, of integreren met een API zonder native node. Hier komt de Code node als redder in nood.

Met de Code node transformeer je N8N van een no-code tool naar een low-code powerhouse. Je kunt letterlijk alles wat JavaScript of Python kan direct in je workflow gebruiken. Van simpele string manipulaties tot complexe machine learning modellen - de mogelijkheden zijn oneindig.

JavaScript vs Python: Welke Kiezen?

AspectJavaScriptPython
Performance⚡ Sneller (native V8)🐢 Langzamer (compilatie overhead)
Use CasesWeb APIs, JSON, DOM, asyncData science, ML, scripting
LibrariesNPM packages (beperkt)Basis libraries (geen pip)
SyntaxC-style, flexibelClean, readable
N8N Integratie✅ Perfect geïntegreerd✅ Goed ondersteund
Debuggingconsole.log() werktprint() output zichtbaar

Advies:

  • 🟨 Kies JavaScript voor: web scraping, API calls, JSON manipulatie, snelle data transformaties
  • 🐍 Kies Python voor: data analyse, machine learning, wetenschappelijke berekeningen, legacy script integratie

Code Node Execution Modes

De Code node heeft twee fundamentele uitvoeringsmodes die bepalen hoe je code wordt uitgevoerd:

Mode 1: Run Once for All Items (Default)

// JavaScript - Process alle items tegelijk
const allItems = $input.all();

// Transform all items
const transformedItems = allItems.map(item => {
  return {
    json: {
      ...item.json,
      processed: true,
      timestamp: new Date().toISOString(),
      total: item.json.price * item.json.quantity
    }
  };
});

return transformedItems;

Gebruik voor:

  • Data aggregatie (som, gemiddelde, max/min)
  • Batch processing
  • Items filteren op basis van andere items
  • Sorteren van complete dataset
  • Deduplicatie

Mode 2: Run Once for Each Item

// JavaScript - Process elk item individueel
const currentItem = $json;

// Transform single item
const processedItem = {
  ...currentItem,
  status: currentItem.score > 80 ? 'passed' : 'failed',
  grade: calculateGrade(currentItem.score),
  processedAt: Date.now()
};

function calculateGrade(score) {
  if (score >= 90) return 'A';
  if (score >= 80) return 'B';
  if (score >= 70) return 'C';
  if (score >= 60) return 'D';
  return 'F';
}

return processedItem;

Gebruik voor:

  • Individuele item transformaties
  • Conditionele processing per item
  • API calls per item
  • Complex business logic per record

Essential Built-in Variables & Methods

Core Variables

VariableBeschrijvingVoorbeeld
$input.all()Alle input items (array)const items = $input.all()
$jsonHuidige item data (each mode)const name = $json.name
$nodeNode metadata$node["NodeName"].json
$workflowWorkflow info$workflow.name
$executionExecution metadata$execution.id
$envEnvironment variables$env.API_KEY
$todayVandaag datum$today.format('YYYY-MM-DD')
$nowHuidige timestamp$now.toISOString()

Praktische JavaScript Voorbeelden

1. Data Aggregatie & Statistieken

// Bereken statistieken over alle orders
const orders = $input.all();

const stats = {
  totalOrders: orders.length,
  totalRevenue: 0,
  averageOrderValue: 0,
  topProduct: {},
  ordersByStatus: {},
  ordersByMonth: {}
};

// Product frequency tracker
const productCounts = {};

orders.forEach(order => {
  const orderData = order.json;
  
  // Revenue calculation
  stats.totalRevenue += orderData.total || 0;
  
  // Status grouping
  stats.ordersByStatus[orderData.status] = 
    (stats.ordersByStatus[orderData.status] || 0) + 1;
  
  // Monthly grouping
  const month = new Date(orderData.date).toISOString().slice(0, 7);
  stats.ordersByMonth[month] = 
    (stats.ordersByMonth[month] || 0) + 1;
  
  // Track products
  orderData.products?.forEach(product => {
    productCounts[product.name] = 
      (productCounts[product.name] || 0) + product.quantity;
  });
});

// Calculate averages and find top product
stats.averageOrderValue = stats.totalRevenue / stats.totalOrders;
stats.topProduct = Object.entries(productCounts)
  .sort((a, b) => b[1] - a[1])[0] || ['None', 0];

return [{ json: stats }];

2. Advanced Data Transformation

// Transform en valideer customer data
const customers = $input.all();

const transformedCustomers = customers.map(item => {
  const customer = item.json;
  
  // Email validation
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  const isValidEmail = emailRegex.test(customer.email);
  
  // Phone formatting (Dutch format)
  const formatPhone = (phone) => {
    const cleaned = phone?.replace(/\D/g, '');
    if (cleaned?.startsWith('31')) {
      return `+${cleaned.slice(0, 2)} ${cleaned.slice(2, 3)} ${cleaned.slice(3)}`;
    }
    return phone;
  };
  
  // Calculate customer lifetime value
  const calculateCLV = (orders) => {
    if (!orders || orders.length === 0) return 0;
    return orders.reduce((sum, order) => sum + order.amount, 0);
  };
  
  // Risk scoring based on multiple factors
  const calculateRiskScore = () => {
    let score = 0;
    if (!isValidEmail) score += 30;
    if (!customer.phone) score += 20;
    if (customer.orders?.length === 0) score += 25;
    if (customer.chargebacks > 0) score += 25;
    return Math.min(score, 100);
  };
  
  return {
    json: {
      id: customer.id,
      fullName: `${customer.firstName} ${customer.lastName}`.trim(),
      email: customer.email?.toLowerCase(),
      emailValid: isValidEmail,
      phone: formatPhone(customer.phone),
      customerSince: customer.createdAt,
      daysSinceSignup: Math.floor(
        (Date.now() - new Date(customer.createdAt)) / (1000 * 60 * 60 * 24)
      ),
      lifetimeValue: calculateCLV(customer.orders),
      orderCount: customer.orders?.length || 0,
      averageOrderValue: customer.orders?.length 
        ? calculateCLV(customer.orders) / customer.orders.length 
        : 0,
      riskScore: calculateRiskScore(),
      segment: determineSegment(calculateCLV(customer.orders)),
      tags: generateTags(customer),
      processedAt: new Date().toISOString()
    }
  };
});

function determineSegment(clv) {
  if (clv > 10000) return 'VIP';
  if (clv > 5000) return 'Gold';
  if (clv > 1000) return 'Silver';
  if (clv > 0) return 'Bronze';
  return 'New';
}

function generateTags(customer) {
  const tags = [];
  if (customer.newsletter) tags.push('subscriber');
  if (customer.referralSource) tags.push(`source:${customer.referralSource}`);
  if (customer.loyaltyPoints > 1000) tags.push('loyal');
  if (customer.lastOrderDays > 90) tags.push('dormant');
  return tags;
}

return transformedCustomers;

3. API Data Merging & Enrichment

// Merge data van multiple sources
const primaryData = $input.all();
const enrichmentAPI = 'https://api.enrichment.com/v1';

// Simulated API responses (in real scenario, use HTTP Request node)
const apiCache = {
  'company': { industry: 'Tech', size: '100-500', revenue: '10M-50M' },
  'social': { linkedin: '/company/example', twitter: '@example' },
  'location': { country: 'NL', city: 'Amsterdam', timezone: 'CET' }
};

const enrichedData = primaryData.map(item => {
  const record = item.json;
  
  // Enrich with company data
  const companyInfo = apiCache.company || {};
  
  // Enrich with social profiles
  const socialProfiles = apiCache.social || {};
  
  // Enrich with location data
  const locationData = apiCache.location || {};
  
  // Calculate lead score based on enriched data
  const calculateLeadScore = () => {
    let score = 0;
    
    // Company size scoring
    if (companyInfo.size?.includes('500')) score += 30;
    else if (companyInfo.size?.includes('100')) score += 20;
    
    // Revenue scoring
    if (companyInfo.revenue?.includes('50M')) score += 40;
    else if (companyInfo.revenue?.includes('10M')) score += 25;
    
    // Engagement scoring
    if (record.emailOpens > 5) score += 15;
    if (record.websiteVisits > 10) score += 15;
    
    return Math.min(score, 100);
  };
  
  return {
    json: {
      // Original data
      ...record,
      
      // Enriched company data
      company: {
        name: record.companyName,
        domain: record.email?.split('@')[1],
        ...companyInfo
      },
      
      // Social profiles
      social: socialProfiles,
      
      // Location enrichment
      location: {
        ...locationData,
        localTime: new Date().toLocaleString('nl-NL', 
          { timeZone: locationData.timezone || 'Europe/Amsterdam' }
        )
      },
      
      // Calculated fields
      leadScore: calculateLeadScore(),
      priority: calculateLeadScore() > 70 ? 'High' : 
                calculateLeadScore() > 40 ? 'Medium' : 'Low',
      
      // Metadata
      enrichedAt: new Date().toISOString(),
      enrichmentVersion: '2.0'
    }
  };
});

return enrichedData;

Python Code Voorbeelden

1. Data Science & Analytics

# Python - Statistical analysis
import json
import statistics
from datetime import datetime, timedelta

items = _input.all()
data_points = [item['json']['value'] for item in items]

# Calculate advanced statistics
result = {
    'count': len(data_points),
    'mean': statistics.mean(data_points),
    'median': statistics.median(data_points),
    'mode': statistics.mode(data_points) if len(set(data_points)) < len(data_points) else None,
    'std_dev': statistics.stdev(data_points) if len(data_points) > 1 else 0,
    'variance': statistics.variance(data_points) if len(data_points) > 1 else 0,
    'min': min(data_points),
    'max': max(data_points),
    'range': max(data_points) - min(data_points),
    'quartiles': {
        'q1': statistics.quantiles(data_points, n=4)[0] if len(data_points) > 3 else None,
        'q2': statistics.median(data_points),
        'q3': statistics.quantiles(data_points, n=4)[2] if len(data_points) > 3 else None
    },
    'outliers': detect_outliers(data_points),
    'trend': calculate_trend(data_points)
}

def detect_outliers(data):
    if len(data) < 4:
        return []
    
    q1 = statistics.quantiles(data, n=4)[0]
    q3 = statistics.quantiles(data, n=4)[2]
    iqr = q3 - q1
    lower_bound = q1 - 1.5 * iqr
    upper_bound = q3 + 1.5 * iqr
    
    return [x for x in data if x < lower_bound or x > upper_bound]

def calculate_trend(data):
    if len(data) < 2:
        return 'insufficient_data'
    
    # Simple trend detection
    first_half = statistics.mean(data[:len(data)//2])
    second_half = statistics.mean(data[len(data)//2:])
    
    if second_half > first_half * 1.1:
        return 'increasing'
    elif second_half < first_half * 0.9:
        return 'decreasing'
    else:
        return 'stable'

return [{'json': result}]

2. Text Processing & NLP

# Python - Text analysis and processing
import re
from collections import Counter

items = _input.all()

processed_items = []

for item in items:
    text = item['json']['text']
    
    # Basic text statistics
    words = text.split()
    sentences = re.split(r'[.!?]+', text)
    
    # Word frequency analysis
    word_freq = Counter(words)
    top_words = word_freq.most_common(10)
    
    # Sentiment indicators (basic)
    positive_words = ['good', 'great', 'excellent', 'amazing', 'perfect', 'love']
    negative_words = ['bad', 'terrible', 'awful', 'horrible', 'hate', 'worst']
    
    positive_count = sum(1 for word in words if word.lower() in positive_words)
    negative_count = sum(1 for word in words if word.lower() in negative_words)
    
    sentiment = 'positive' if positive_count > negative_count else \
                'negative' if negative_count > positive_count else 'neutral'
    
    # Extract patterns
    emails = re.findall(r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b', text)
    urls = re.findall(r'http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+', text)
    phone_numbers = re.findall(r'\b\d{10}\b|\+\d{1,3}\s?\d{1,14}', text)
    
    processed_items.append({
        'json': {
            'original_text': text,
            'word_count': len(words),
            'sentence_count': len([s for s in sentences if s.strip()]),
            'avg_word_length': sum(len(word) for word in words) / len(words) if words else 0,
            'unique_words': len(set(words)),
            'lexical_diversity': len(set(words)) / len(words) if words else 0,
            'top_words': top_words,
            'sentiment': sentiment,
            'sentiment_scores': {
                'positive': positive_count,
                'negative': negative_count,
                'neutral': len(words) - positive_count - negative_count
            },
            'extracted_data': {
                'emails': emails,
                'urls': urls,
                'phone_numbers': phone_numbers
            },
            'readability_score': calculate_readability(text),
            'processed_at': datetime.now().isoformat()
        }
    })

def calculate_readability(text):
    # Flesch Reading Ease approximation
    words = text.split()
    sentences = re.split(r'[.!?]+', text)
    syllables = sum(count_syllables(word) for word in words)
    
    if len(sentences) == 0 or len(words) == 0:
        return 0
    
    score = 206.835 - 1.015 * (len(words) / len(sentences)) - 84.6 * (syllables / len(words))
    return max(0, min(100, score))

def count_syllables(word):
    # Simple syllable counter
    word = word.lower()
    vowels = 'aeiou'
    syllable_count = 0
    previous_was_vowel = False
    
    for char in word:
        is_vowel = char in vowels
        if is_vowel and not previous_was_vowel:
            syllable_count += 1
        previous_was_vowel = is_vowel
    
    return max(1, syllable_count)

return processed_items

Advanced Patterns & Techniques

1. Error Handling & Validation

// Robuuste error handling
const items = $input.all();
const results = [];
const errors = [];

items.forEach((item, index) => {
  try {
    // Validate required fields
    const required = ['email', 'name', 'amount'];
    const missing = required.filter(field => !item.json[field]);
    
    if (missing.length > 0) {
      throw new Error(`Missing required fields: ${missing.join(', ')}`);
    }
    
    // Validate data types
    if (typeof item.json.amount !== 'number') {
      throw new Error(`Amount must be a number, got ${typeof item.json.amount}`);
    }
    
    // Validate email format
    if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(item.json.email)) {
      throw new Error(`Invalid email format: ${item.json.email}`);
    }
    
    // Process valid item
    results.push({
      json: {
        ...item.json,
        validated: true,
        processedAt: new Date().toISOString()
      }
    });
    
  } catch (error) {
    // Log error but continue processing
    errors.push({
      json: {
        originalItem: item.json,
        error: error.message,
        itemIndex: index,
        failedAt: new Date().toISOString()
      }
    });
  }
});

// Return both successful and failed items
return [
  ...results,
  ...errors.map(e => ({ ...e, json: { ...e.json, _error: true } }))
];

2. Async Operations & Promises

// Async processing met promises
const processItemsAsync = async () => {
  const items = $input.all();
  
  // Simulate async operations (API calls, database queries)
  const processItem = async (item) => {
    // Simulate API delay
    await new Promise(resolve => setTimeout(resolve, 100));
    
    // Async processing logic
    const enrichedData = {
      ...item.json,
      asyncProcessed: true,
      processingTime: 100,
      enrichedAt: new Date().toISOString()
    };
    
    // Simulate occasional failures
    if (Math.random() > 0.95) {
      throw new Error('Random processing failure');
    }
    
    return { json: enrichedData };
  };
  
  // Process items in parallel with error handling
  const promises = items.map(async (item, index) => {
    try {
      const result = await processItem(item);
      return { success: true, data: result };
    } catch (error) {
      return { 
        success: false, 
        data: { 
          json: { 
            ...item.json, 
            error: error.message,
            failedIndex: index 
          } 
        } 
      };
    }
  });
  
  const results = await Promise.all(promises);
  
  // Separate successful and failed items
  const successful = results
    .filter(r => r.success)
    .map(r => r.data);
  
  const failed = results
    .filter(r => !r.success)
    .map(r => r.data);
  
  // Add summary
  const summary = {
    json: {
      totalProcessed: items.length,
      successful: successful.length,
      failed: failed.length,
      successRate: (successful.length / items.length * 100).toFixed(2) + '%',
      timestamp: new Date().toISOString()
    }
  };
  
  return [...successful, ...failed, summary];
};

// Return promise that N8N will resolve
return processItemsAsync();

3. Dynamic Code Generation

// Dynamisch SQL queries genereren
const items = $input.all();
const tableName = 'customers';

const generateSQL = (operation, data) => {
  switch(operation) {
    case 'INSERT':
      const columns = Object.keys(data).join(', ');
      const values = Object.values(data)
        .map(v => typeof v === 'string' ? `'${v}'` : v)
        .join(', ');
      return `INSERT INTO ${tableName} (${columns}) VALUES (${values});`;
    
    case 'UPDATE':
      const updates = Object.entries(data)
        .filter(([key]) => key !== 'id')
        .map(([key, value]) => {
          const val = typeof value === 'string' ? `'${value}'` : value;
          return `${key} = ${val}`;
        })
        .join(', ');
      return `UPDATE ${tableName} SET ${updates} WHERE id = ${data.id};`;
    
    case 'DELETE':
      return `DELETE FROM ${tableName} WHERE id = ${data.id};`;
    
    default:
      throw new Error(`Unknown operation: ${operation}`);
  }
};

const sqlQueries = items.map(item => {
  const operation = item.json.operation || 'INSERT';
  const query = generateSQL(operation, item.json.data);
  
  return {
    json: {
      operation,
      query,
      data: item.json.data,
      generated: true,
      timestamp: new Date().toISOString()
    }
  };
});

// Add batch transaction wrapper
const transaction = {
  json: {
    type: 'transaction',
    queries: sqlQueries.map(q => q.json.query),
    fullSQL: [
      'BEGIN TRANSACTION;',
      ...sqlQueries.map(q => q.json.query),
      'COMMIT;'
    ].join('\n')
  }
};

return [...sqlQueries, transaction];

Performance Optimalisatie Tips

1. Memory Management

// Efficient memory usage voor grote datasets
const items = $input.all();
const CHUNK_SIZE = 1000;

// Process in chunks to avoid memory issues
const processInChunks = () => {
  const results = [];
  
  for (let i = 0; i < items.length; i += CHUNK_SIZE) {
    const chunk = items.slice(i, i + CHUNK_SIZE);
    
    // Process chunk
    const processed = chunk.map(item => ({
      json: {
        id: item.json.id,
        processed: true
        // Minimal data retention
      }
    }));
    
    results.push(...processed);
    
    // Clear references for garbage collection
    chunk.length = 0;
  }
  
  return results;
};

return processInChunks();

2. Caching Strategieën

// Implement caching voor expensive operations
const cache = new Map();

const expensiveOperation = (input) => {
  // Check cache first
  const cacheKey = JSON.stringify(input);
  if (cache.has(cacheKey)) {
    return cache.get(cacheKey);
  }
  
  // Expensive calculation
  const result = {
    calculated: input.value * Math.random() * 1000,
    timestamp: Date.now()
  };
  
  // Store in cache
  cache.set(cacheKey, result);
  
  // Limit cache size
  if (cache.size > 100) {
    const firstKey = cache.keys().next().value;
    cache.delete(firstKey);
  }
  
  return result;
};

const items = $input.all();
return items.map(item => ({
  json: {
    ...item.json,
    ...expensiveOperation(item.json)
  }
}));

Integratie met External Libraries

JavaScript NPM Packages

N8N ondersteunt een beperkte set NPM packages. Enkele bruikbare:

  • crypto - Encryption, hashing
  • lodash - Utility functions
  • moment - Date manipulation
  • uuid - Unique identifiers
// Gebruik van crypto library
const crypto = require('crypto');

const items = $input.all();

return items.map(item => {
  // Generate unique ID
  const id = crypto.randomUUID();
  
  // Hash sensitive data
  const hash = crypto
    .createHash('sha256')
    .update(item.json.email)
    .digest('hex');
  
  // Encrypt data
  const algorithm = 'aes-256-cbc';
  const key = crypto.scryptSync($env.ENCRYPTION_KEY, 'salt', 32);
  const iv = crypto.randomBytes(16);
  const cipher = crypto.createCipheriv(algorithm, key, iv);
  
  let encrypted = cipher.update(JSON.stringify(item.json.sensitive), 'utf8', 'hex');
  encrypted += cipher.final('hex');
  
  return {
    json: {
      id,
      emailHash: hash,
      encryptedData: encrypted,
      iv: iv.toString('hex'),
      ...item.json
    }
  };
});

Debugging & Testing

Console Logging

// Effective debugging techniques
const items = $input.all();

// Log to console (visible in browser console)
console.log('Starting processing:', items.length, 'items');
console.table(items.slice(0, 5)); // Table format voor eerste 5

// Create debug output
const debugInfo = {
  itemCount: items.length,
  firstItem: items[0]?.json,
  lastItem: items[items.length - 1]?.json,
  memoryUsage: process.memoryUsage ? process.memoryUsage() : 'N/A',
  timestamp: new Date().toISOString()
};

console.log('Debug Info:', debugInfo);

// Add debug data to output
return [
  { json: { _debug: debugInfo } },
  ...items
];

Unit Testing Pattern

// Test framework binnen Code node
const runTests = () => {
  const tests = [];
  
  // Test 1: Email validation
  const testEmailValidation = () => {
    const validEmails = ['test@example.com', 'user+tag@domain.co.uk'];
    const invalidEmails = ['notanemail', '@example.com', 'test@'];
    
    const validateEmail = (email) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
    
    const results = {
      valid: validEmails.every(e => validateEmail(e)),
      invalid: invalidEmails.every(e => !validateEmail(e))
    };
    
    return {
      name: 'Email Validation',
      passed: results.valid && results.invalid,
      details: results
    };
  };
  
  // Test 2: Data transformation
  const testDataTransformation = () => {
    const input = { price: 100, quantity: 2 };
    const expected = { price: 100, quantity: 2, total: 200 };
    
    const transform = (data) => ({
      ...data,
      total: data.price * data.quantity
    });
    
    const result = transform(input);
    
    return {
      name: 'Data Transformation',
      passed: result.total === expected.total,
      details: { input, result, expected }
    };
  };
  
  // Run all tests
  tests.push(testEmailValidation());
  tests.push(testDataTransformation());
  
  // Summary
  const summary = {
    totalTests: tests.length,
    passed: tests.filter(t => t.passed).length,
    failed: tests.filter(t => !t.passed).length,
    tests
  };
  
  return summary;
};

const testResults = runTests();
console.log('Test Results:', testResults);

return [{ json: testResults }];

Best Practices & Common Pitfalls

✅ DO's:

  • ✅ Gebruik $input.all() voor batch processing
  • ✅ Return altijd een array van objecten met json key
  • ✅ Implementeer error handling voor robuustheid
  • ✅ Gebruik environment variables voor configuratie
  • ✅ Test met kleine datasets eerst
  • ✅ Comment complex logic voor maintainability
  • ✅ Gebruik built-in N8N variables waar mogelijk

❌ DON'Ts:

  • ❌ Geen filesystem access (niet ondersteund)
  • ❌ Geen directe HTTP requests (gebruik HTTP Request node)
  • ❌ Geen infinite loops of recursie
  • ❌ Geen heavy CPU operations (timeout risico)
  • ❌ Geen externe package installatie (alleen pre-installed)
  • ❌ Geen global state tussen executions

Real-World Use Cases

Use CaseLanguageCode Focus
E-commerce order processingJavaScriptValidation, calculations, formatting
Data cleaning & normalizationJavaScriptRegex, string manipulation
Statistical analysisPythonMath, statistics libraries
Lead scoring & qualificationJavaScriptBusiness logic, scoring algorithms
Report generationJavaScriptAggregation, formatting
API response transformationJavaScriptJSON manipulation, mapping
Time series analysisPythonDate handling, trend detection
Custom encryption/hashingJavaScriptCrypto library usage

Conclusie & Next Steps

De Code node transformeert N8N van een no-code tool naar een volledig programmeerbare automatisering platform. Met JavaScript voor snelle web-gerelateerde taken en Python voor data science, heb je alle tools om complexe business logic te implementeren.

Learning Path:

  1. Start met simpele data transformaties
  2. Experimenteer met beide execution modes
  3. Leer de built-in variables en methods
  4. Implementeer error handling patterns
  5. Optimaliseer voor performance
  6. Integreer met andere nodes voor complete workflows

💡 Pro Tip: Begin altijd met console.log($input.all()) om de data structuur te begrijpen. Test je code met een kleine subset voordat je het op grote datasets loslaat. Gebruik de browser console voor debugging - alle console.log output is daar zichtbaar!

#n8n #code node #javascript #python #custom logic #data transformation #automation #scripting #tutorial