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?
| Aspect | JavaScript | Python |
|---|---|---|
| Performance | ⚡ Sneller (native V8) | 🐢 Langzamer (compilatie overhead) |
| Use Cases | Web APIs, JSON, DOM, async | Data science, ML, scripting |
| Libraries | NPM packages (beperkt) | Basis libraries (geen pip) |
| Syntax | C-style, flexibel | Clean, readable |
| N8N Integratie | ✅ Perfect geïntegreerd | ✅ Goed ondersteund |
| Debugging | console.log() werkt | print() 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
| Variable | Beschrijving | Voorbeeld |
|---|---|---|
| $input.all() | Alle input items (array) | const items = $input.all() |
| $json | Huidige item data (each mode) | const name = $json.name |
| $node | Node metadata | $node["NodeName"].json |
| $workflow | Workflow info | $workflow.name |
| $execution | Execution metadata | $execution.id |
| $env | Environment variables | $env.API_KEY |
| $today | Vandaag datum | $today.format('YYYY-MM-DD') |
| $now | Huidige 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_itemsAdvanced 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
jsonkey - ✅ 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 Case | Language | Code Focus |
|---|---|---|
| E-commerce order processing | JavaScript | Validation, calculations, formatting |
| Data cleaning & normalization | JavaScript | Regex, string manipulation |
| Statistical analysis | Python | Math, statistics libraries |
| Lead scoring & qualification | JavaScript | Business logic, scoring algorithms |
| Report generation | JavaScript | Aggregation, formatting |
| API response transformation | JavaScript | JSON manipulation, mapping |
| Time series analysis | Python | Date handling, trend detection |
| Custom encryption/hashing | JavaScript | Crypto 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:
- Start met simpele data transformaties
- Experimenteer met beide execution modes
- Leer de built-in variables en methods
- Implementeer error handling patterns
- Optimaliseer voor performance
- 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!