mirror of
https://github.com/czlonkowski/n8n-skills.git
synced 2026-03-16 23:43:08 +00:00
BREAKING: Updated all skills to reflect n8n-mcp tool consolidation:
## Tool API Changes
- get_node_essentials → get_node({detail: "standard"})
- get_node_info → get_node({detail: "full"})
- get_node_documentation → get_node({mode: "docs"})
- search_node_properties → get_node({mode: "search_properties"})
- validate_node_minimal → validate_node({mode: "minimal"})
- validate_node_operation → validate_node({mode: "full"})
- get_property_dependencies → REMOVED (use get_node modes)
## New Features Documented
- Workflow activation via API (activateWorkflow/deactivateWorkflow operations)
- n8n_deploy_template - deploy templates directly to n8n
- n8n_workflow_versions - version history and rollback
- n8n_test_workflow - trigger execution
- n8n_executions - manage executions
- Smart parameters (branch, case) for IF/Switch connections
- Intent parameter for better AI responses
## Documentation Updates
- Added YouTube video introduction with thumbnail
- Added GitHub stars badge (1.2k milestone)
- Added build.sh script for dist packaging
- Fixed "5 skills" → "7 skills" inconsistency in README
## Files Updated
- n8n-mcp-tools-expert: Complete rewrite of SKILL.md, SEARCH_GUIDE.md,
VALIDATION_GUIDE.md, WORKFLOW_GUIDE.md
- n8n-node-configuration: Updated SKILL.md, DEPENDENCIES.md
- n8n-validation-expert: Updated SKILL.md, ERROR_CATALOG.md, FALSE_POSITIVES.md
- n8n-workflow-patterns: Updated SKILL.md, README.md
- README.md, CLAUDE.md: Modernized documentation
Conceived by Romuald Członkowski - www.aiadvisors.pl/en
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1205 lines
34 KiB
Markdown
1205 lines
34 KiB
Markdown
# n8n CODE Node - Best Practices & Pattern Analysis
|
|
|
|
**Analysis Period:** Last 60 days | **Data Quality:** 38,094 CODE node instances analyzed
|
|
|
|
---
|
|
|
|
## Executive Summary
|
|
|
|
- **47.16% of workflows use CODE nodes** (15,202 workflows from 4,461 unique users)
|
|
- **Top 3 data access patterns:** `$input.all()` (26% usage), `$input.first()` (25%), `$input.item` (19%)
|
|
- **Critical finding:** 39% validation failures are due to empty code or missing return values
|
|
- **Best return pattern:** `return [{json: {...}}]` (39% of successful nodes)
|
|
|
|
---
|
|
|
|
## 1. Node Configuration Essentials
|
|
|
|
### Choosing the Right Mode
|
|
|
|
The Code node offers two execution modes that determine how your code processes input data:
|
|
|
|
#### **Run Once for All Items** (Default - Recommended for most use cases)
|
|
- Code executes **once** regardless of input count
|
|
- Access all items via `$input.all()` or `items` array
|
|
- **Best for:** Aggregation, filtering, batch processing, transformations
|
|
- **Performance:** Faster for multiple items (single execution)
|
|
- **Usage:** 78% of successful workflows use this mode
|
|
|
|
```javascript
|
|
// Example: Process all items together
|
|
const allItems = $input.all();
|
|
const total = allItems.reduce((sum, item) => sum + item.json.amount, 0);
|
|
return [{json: {total, count: allItems.length}}];
|
|
```
|
|
|
|
#### **Run Once for Each Item**
|
|
- Code executes **separately** for each input item
|
|
- Access current item via `$input.item` or `$item`
|
|
- **Best for:** Item-specific logic, independent operations, item validation
|
|
- **Performance:** Slower for large datasets (multiple executions)
|
|
- **Usage:** 22% of workflows (specialized cases)
|
|
|
|
```javascript
|
|
// Example: Process each item independently
|
|
const item = $input.item;
|
|
return [{
|
|
json: {
|
|
...item.json,
|
|
processed: true,
|
|
processedAt: new Date().toISOString()
|
|
}
|
|
}];
|
|
```
|
|
|
|
**Decision Guide:**
|
|
- ✅ Use "All Items" when: Comparing items, calculating totals, sorting, deduplication
|
|
- ✅ Use "Each Item" when: Item-specific API calls, individual validations, per-item errors
|
|
|
|
---
|
|
|
|
### Language Selection
|
|
|
|
| Language | Use Case | Performance | Built-ins | Beta Status |
|
|
|----------|----------|-------------|-----------|-------------|
|
|
| **JavaScript** | General purpose, web APIs, JSON | Fast | Full n8n helpers | Stable |
|
|
| **Python (Beta)** | Data science, ML, complex math | Slower | `_` syntax helpers | Beta |
|
|
| **Python (Native)** | Standard Python, no helpers | Medium | `_items`, `_item` only | Beta |
|
|
|
|
**Recommendation:** Use JavaScript for 95% of use cases. Only use Python when you need specific libraries or data science capabilities.
|
|
|
|
---
|
|
|
|
## 2. Top 10 Successful CODE Node Patterns
|
|
|
|
### Pattern 1: Multi-source Data Aggregation
|
|
```javascript
|
|
// Process and structure collected data from multiple sources
|
|
const allItems = $input.all();
|
|
let processedArticles = [];
|
|
|
|
// Handle different source formats
|
|
for (const item of allItems) {
|
|
const sourceName = item.json.name || 'Unknown';
|
|
const sourceData = item.json;
|
|
|
|
// Parse source-specific structure
|
|
if (sourceName === 'Hacker News' && sourceData.hits) {
|
|
for (const hit of sourceData.hits) {
|
|
processedArticles.push({
|
|
title: hit.title,
|
|
url: hit.url,
|
|
summary: hit.story_text || 'No summary',
|
|
source: 'Hacker News',
|
|
score: hit.points || 0
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
return processedArticles.map(article => ({ json: article }));
|
|
```
|
|
**Use Case:** Aggregating data from APIs, RSS feeds, webhooks
|
|
**Key Techniques:** Loop iteration, conditional parsing, data normalization
|
|
|
|
---
|
|
|
|
### Pattern 2: Regex Filtering & Pattern Matching
|
|
```javascript
|
|
// Extract and filter mentions using regex patterns
|
|
const etfPattern = /\b([A-Z]{2,5})\b/g;
|
|
const knownETFs = ['VOO', 'VTI', 'VT', 'SCHD', 'QYLD', 'VXUS'];
|
|
|
|
const etfMentions = {};
|
|
|
|
for (const item of $input.all()) {
|
|
const data = item.json.data;
|
|
if (!data?.children) continue;
|
|
|
|
for (const post of data.children) {
|
|
const combinedText = (post.data.title + ' ' + post.data.selftext).toUpperCase();
|
|
const matches = combinedText.match(etfPattern);
|
|
|
|
if (matches) {
|
|
for (const match of matches) {
|
|
if (knownETFs.includes(match)) {
|
|
if (!etfMentions[match]) {
|
|
etfMentions[match] = { count: 0, totalScore: 0, posts: [] };
|
|
}
|
|
etfMentions[match].count++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return Object.entries(etfMentions)
|
|
.map(([etf, data]) => ({ json: { etf, ...data } }))
|
|
.sort((a, b) => b.json.count - a.json.count);
|
|
```
|
|
**Use Case:** Content analysis, keyword extraction, mention tracking
|
|
**Key Techniques:** Regex matching, object aggregation, sorting/ranking
|
|
|
|
---
|
|
|
|
### Pattern 3: Markdown Parsing & Structured Data Extraction
|
|
```javascript
|
|
// Parse markdown and extract structured information
|
|
const markdown = $input.first().json.data.markdown;
|
|
const adRegex = /##\s*(.*?)\n(.*?)(?=\n##|\n---|$)/gs;
|
|
|
|
const ads = [];
|
|
let match;
|
|
|
|
function parseTimeToMinutes(timeStr) {
|
|
if (!timeStr) return 999999;
|
|
|
|
const hourMatch = timeStr.match(/(\d+)\s*hour/);
|
|
const dayMatch = timeStr.match(/(\d+)\s*day/);
|
|
|
|
let totalMinutes = 0;
|
|
if (hourMatch) totalMinutes += parseInt(hourMatch[1]) * 60;
|
|
if (dayMatch) totalMinutes += parseInt(dayMatch[1]) * 1440;
|
|
|
|
return totalMinutes;
|
|
}
|
|
|
|
while ((match = adRegex.exec(markdown)) !== null) {
|
|
const title = match[1]?.trim() || 'No title';
|
|
const content = match[2]?.trim() || '';
|
|
|
|
const districtMatch = content.match(/\*\*District:\*\*\s*(.*?)(?:\n|$)/);
|
|
const timeMatch = content.match(/Posted:\s*(.*?)\*/);
|
|
|
|
ads.push({
|
|
title: title,
|
|
district: districtMatch?.[1].trim() || 'Unknown',
|
|
timeInMinutes: parseTimeToMinutes(timeMatch?.[1]),
|
|
fullContent: content
|
|
});
|
|
}
|
|
|
|
return ads.map(ad => ({ json: ad }));
|
|
```
|
|
**Use Case:** Parsing formatted text, extracting structured fields
|
|
**Key Techniques:** Regex grouping, helper functions, data normalization
|
|
|
|
---
|
|
|
|
### Pattern 4: JSON Comparison & Validation
|
|
```javascript
|
|
// Compare and validate JSON objects from different sources
|
|
const orderJsonKeys = (jsonObj) => {
|
|
const ordered = {};
|
|
Object.keys(jsonObj).sort().forEach(key => {
|
|
ordered[key] = jsonObj[key];
|
|
});
|
|
return ordered;
|
|
};
|
|
|
|
const origWorkflow = JSON.parse(
|
|
Buffer.from($input.all()[0].json.content, 'base64').toString()
|
|
);
|
|
const n8nWorkflow = $input.all()[1].json;
|
|
|
|
const orderedOriginal = orderJsonKeys(origWorkflow);
|
|
const orderedActual = orderJsonKeys(n8nWorkflow);
|
|
|
|
const isSame = JSON.stringify(orderedOriginal) === JSON.stringify(orderedActual);
|
|
|
|
$input.all()[0].json.status = isSame ? 'same' : 'different';
|
|
$input.all()[0].json.original_data = orderedOriginal;
|
|
|
|
return $input.all();
|
|
```
|
|
**Use Case:** Workflow versioning, configuration validation, change detection
|
|
**Key Techniques:** JSON ordering, base64 decoding, deep comparison
|
|
|
|
---
|
|
|
|
### Pattern 5: CRM Data Transformation
|
|
```javascript
|
|
// Transform form data into CRM-compatible format
|
|
const item = $input.all()[0];
|
|
const { name, email, phone, company, course_interest, message, timestamp } = item.json;
|
|
|
|
const nameParts = name.split(' ');
|
|
const firstName = nameParts[0] || '';
|
|
const lastName = nameParts.slice(1).join(' ') || 'Unknown';
|
|
|
|
const crmData = {
|
|
data: {
|
|
type: 'Contact',
|
|
attributes: {
|
|
first_name: firstName,
|
|
last_name: lastName,
|
|
email1: email,
|
|
phone_work: phone,
|
|
account_name: company,
|
|
description: `Course: ${course_interest}\nMessage: ${message}\nTimestamp: ${timestamp}`
|
|
}
|
|
}
|
|
};
|
|
|
|
return [{
|
|
json: {
|
|
...item.json,
|
|
crmData,
|
|
processed: true
|
|
}
|
|
}];
|
|
```
|
|
**Use Case:** Lead enrichment, data normalization, API preparation
|
|
**Key Techniques:** Object destructuring, data mapping, format conversion
|
|
|
|
---
|
|
|
|
### Pattern 6: Release Information Processing
|
|
```javascript
|
|
// Extract and filter stable releases from GitHub API
|
|
const allReleases = $input.first().json;
|
|
const stableReleases = allReleases
|
|
.filter(release => !release.prerelease && !release.draft)
|
|
.slice(0, 10)
|
|
.map(release => ({
|
|
tag: release.tag_name,
|
|
name: release.name,
|
|
published: release.published_at,
|
|
publishedDate: new Date(release.published_at).toLocaleDateString(),
|
|
author: release.author.login,
|
|
url: release.html_url,
|
|
changelog: release.body || '(No changelog)',
|
|
highlights: release.body?.split('## Highlights:')[1]?.split('##')[0]?.trim()
|
|
|| release.body?.substring(0, 500) + '...'
|
|
|| 'No highlights available',
|
|
assetCount: release.assets.length
|
|
}));
|
|
|
|
return stableReleases.map(release => ({ json: release }));
|
|
```
|
|
**Use Case:** Version management, changelog parsing, release notes generation
|
|
**Key Techniques:** Array filtering, conditional field extraction, date formatting
|
|
|
|
---
|
|
|
|
### Pattern 7: Array Transformation with Context
|
|
```javascript
|
|
// Transform and map data with additional context
|
|
const stableReleases = $input.first().json
|
|
.filter(release => !release.prerelease && !release.draft)
|
|
.slice(0, 10)
|
|
.map(release => ({
|
|
version: release.tag_name,
|
|
assetCount: release.assets.length,
|
|
assetsCountText: release.assets.length === 1 ? 'file' : 'files'
|
|
}));
|
|
|
|
return stableReleases.map(release => ({ json: release }));
|
|
```
|
|
**Use Case:** Quick data transformation, simple field mapping
|
|
**Key Techniques:** Array methods chaining, pluralization logic
|
|
|
|
---
|
|
|
|
### Pattern 8: Slack Block Kit Formatting
|
|
```javascript
|
|
// Create Slack-formatted message with structured blocks
|
|
const date = new Date().toISOString().split('T')[0];
|
|
|
|
return [{
|
|
json: {
|
|
text: `Daily Report - ${date}`,
|
|
blocks: [
|
|
{
|
|
type: "header",
|
|
text: {
|
|
type: "plain_text",
|
|
text: `📊 Daily Security Report - ${date}`
|
|
}
|
|
},
|
|
{
|
|
type: "section",
|
|
text: {
|
|
type: "mrkdwn",
|
|
text: `*Status:* ✅ All Clear\n*Alerts:* 0\n*Updated:* ${new Date().toLocaleString()}`
|
|
}
|
|
},
|
|
{
|
|
type: "context",
|
|
elements: [{
|
|
type: "mrkdwn",
|
|
text: `Report generated automatically`
|
|
}]
|
|
}
|
|
]
|
|
}
|
|
}];
|
|
```
|
|
**Use Case:** Chat notifications, rich message formatting
|
|
**Key Techniques:** Template literals, nested objects, Block Kit syntax
|
|
|
|
---
|
|
|
|
### Pattern 9: Top N Filtering
|
|
```javascript
|
|
// Filter and rank by score, return top N results
|
|
const ragResponse = $input.item.json;
|
|
const chunks = ragResponse.chunks || [];
|
|
|
|
const topChunks = chunks
|
|
.sort((a, b) => (b.similarity || 0) - (a.similarity || 0))
|
|
.slice(0, 6);
|
|
|
|
return [{
|
|
json: {
|
|
topChunks: topChunks,
|
|
count: topChunks.length
|
|
}
|
|
}];
|
|
```
|
|
**Use Case:** RAG pipelines, ranking algorithms, result filtering
|
|
**Key Techniques:** Sorting, slicing, null coalescing
|
|
|
|
---
|
|
|
|
### Pattern 10: String Aggregation & Reporting
|
|
```javascript
|
|
// Aggregate multiple text inputs into formatted report
|
|
const ragResponse = $input.item.json;
|
|
const markdown = ragResponse.data.markdown;
|
|
|
|
const finalReport = $input.all()
|
|
.map(item => item.json.message)
|
|
.join('\n\n---\n\n');
|
|
|
|
const header = `🎯 **Report**\n📅 ${new Date().toLocaleString()}\n\n`;
|
|
|
|
return [{
|
|
json: {
|
|
report: header + finalReport,
|
|
timestamp: new Date().toISOString()
|
|
}
|
|
}];
|
|
```
|
|
**Use Case:** Report generation, log aggregation, content concatenation
|
|
**Key Techniques:** Array joining, string concatenation, timestamp handling
|
|
|
|
---
|
|
|
|
## 2. Python Code Examples & Best Practices
|
|
|
|
### Python vs JavaScript: Key Differences
|
|
|
|
| Feature | JavaScript | Python (Beta) | Python (Native) |
|
|
|---------|-----------|---------------|-----------------|
|
|
| Input access | `$input.all()` | `_input.all()` | `_items` |
|
|
| Single item | `$input.first()` | `_input.first()` | `_items[0]` |
|
|
| Current item | `$input.item` | `_input.item` | `_item` |
|
|
| Return format | `[{json: {...}}]` | `[{json: {...}}]` | `[{"json": {...}}]` |
|
|
| Date helper | `$now` | `_now` | Standard datetime |
|
|
| JSON query | `$jmespath()` | `_jmespath()` | Not available |
|
|
|
|
### Python Pattern 1: Data Transformation (Run Once for All Items)
|
|
|
|
```python
|
|
# Python (Beta) - Using n8n helpers
|
|
items = _input.all()
|
|
processed = []
|
|
|
|
for item in items:
|
|
data = item["json"]
|
|
processed.append({
|
|
"json": {
|
|
"id": data.get("id"),
|
|
"name": data.get("name", "Unknown"),
|
|
"processed": True,
|
|
"timestamp": _now.isoformat()
|
|
}
|
|
})
|
|
|
|
return processed
|
|
```
|
|
|
|
```python
|
|
# Python (Native) - Standard Python
|
|
processed = []
|
|
|
|
for item in _items:
|
|
data = item["json"]
|
|
processed.append({
|
|
"json": {
|
|
"id": data.get("id"),
|
|
"name": data.get("name", "Unknown"),
|
|
"processed": True,
|
|
"timestamp": str(_now) # _now is datetime object
|
|
}
|
|
})
|
|
|
|
return processed
|
|
```
|
|
|
|
### Python Pattern 2: Filtering & Aggregation
|
|
|
|
```python
|
|
# Filter and sum amounts
|
|
items = _input.all()
|
|
total = 0
|
|
valid_items = []
|
|
|
|
for item in items:
|
|
amount = item["json"].get("amount", 0)
|
|
if amount > 0:
|
|
total += amount
|
|
valid_items.append(item["json"])
|
|
|
|
return [{
|
|
"json": {
|
|
"total": total,
|
|
"count": len(valid_items),
|
|
"items": valid_items
|
|
}
|
|
}]
|
|
```
|
|
|
|
### Python Pattern 3: String Processing with Regex
|
|
|
|
```python
|
|
import re
|
|
|
|
# Extract emails from text
|
|
items = _input.all()
|
|
email_pattern = r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b'
|
|
|
|
all_emails = []
|
|
for item in items:
|
|
text = item["json"].get("text", "")
|
|
emails = re.findall(email_pattern, text)
|
|
all_emails.extend(emails)
|
|
|
|
# Remove duplicates
|
|
unique_emails = list(set(all_emails))
|
|
|
|
return [{
|
|
"json": {
|
|
"emails": unique_emails,
|
|
"count": len(unique_emails)
|
|
}
|
|
}]
|
|
```
|
|
|
|
### Python Pattern 4: Data Science Operations
|
|
|
|
```python
|
|
# Calculate statistics (Python Native with standard library)
|
|
from statistics import mean, median, stdev
|
|
|
|
items = _items
|
|
values = [item["json"].get("value", 0) for item in items if "value" in item["json"]]
|
|
|
|
if len(values) > 0:
|
|
return [{
|
|
"json": {
|
|
"mean": mean(values),
|
|
"median": median(values),
|
|
"std_dev": stdev(values) if len(values) > 1 else 0,
|
|
"min": min(values),
|
|
"max": max(values),
|
|
"count": len(values)
|
|
}
|
|
}]
|
|
else:
|
|
return [{"json": {"error": "No values found"}}]
|
|
```
|
|
|
|
### Python Pattern 5: Dictionary/Object Manipulation
|
|
|
|
```python
|
|
# Merge and deduplicate objects by key
|
|
items = _input.all()
|
|
merged = {}
|
|
|
|
for item in items:
|
|
data = item["json"]
|
|
key = data.get("id")
|
|
|
|
if key:
|
|
if key not in merged:
|
|
merged[key] = data
|
|
else:
|
|
# Merge properties, preferring newer values
|
|
merged[key].update({k: v for k, v in data.items() if v})
|
|
|
|
# Convert back to array format
|
|
result = [{"json": value} for value in merged.values()]
|
|
return result
|
|
```
|
|
|
|
### Python Best Practices
|
|
|
|
1. **Always use `.get()` for dictionary access** to avoid KeyError
|
|
```python
|
|
# ✅ Safe
|
|
value = item["json"].get("field", "default")
|
|
|
|
# ❌ Risky
|
|
value = item["json"]["field"] # Crashes if field missing
|
|
```
|
|
|
|
2. **Handle None/null values explicitly**
|
|
```python
|
|
amount = item["json"].get("amount") or 0 # Default to 0
|
|
text = item["json"].get("text", "").strip() # Default to empty string
|
|
```
|
|
|
|
3. **Use list comprehensions for filtering**
|
|
```python
|
|
# ✅ Pythonic
|
|
valid = [item for item in items if item["json"].get("active")]
|
|
|
|
# ❌ Verbose
|
|
valid = []
|
|
for item in items:
|
|
if item["json"].get("active"):
|
|
valid.append(item)
|
|
```
|
|
|
|
4. **Return consistent structure**
|
|
```python
|
|
# Always return list of objects with "json" key
|
|
return [{"json": result}] # Single result
|
|
return results # Multiple results (already formatted)
|
|
return [] # No results
|
|
```
|
|
|
|
5. **Debug with print() statements**
|
|
```python
|
|
print(f"Processing {len(items)} items") # Appears in browser console
|
|
print(f"Item data: {item['json']}")
|
|
```
|
|
|
|
---
|
|
|
|
## 3. Common Data Access Patterns
|
|
|
|
### Usage Distribution
|
|
| Pattern | Usage % | Best For |
|
|
|---------|---------|----------|
|
|
| `$input.all()` | 26% | Processing arrays, batch operations |
|
|
| `$input.first()` | 25% | Single item operations, first-last logic |
|
|
| `$input.item` | 19% | Item-by-item processing in loops |
|
|
| Other patterns | 16% | Complex scenarios |
|
|
| `$json` | 11% | Direct field access |
|
|
| `$node` | 1% | Referencing other node outputs |
|
|
| `$env` | 0.2% | Environment variables |
|
|
| `$binary` | 0.1% | Binary data processing |
|
|
|
|
### Key Recommendations
|
|
1. **Use `$input.all()` when:** Processing multiple records, aggregating data, batch transformations
|
|
2. **Use `$input.first()` when:** Working with single objects, API responses, initial data processing
|
|
3. **Use `$input.item` when:** In split/loop contexts, iterating collections, item-by-item logic
|
|
4. **Avoid `$json` alone:** Always use `$input.first().json` or `$input.item.json` to ensure data availability
|
|
|
|
---
|
|
|
|
## 3. Return Value Structures
|
|
|
|
### Recommended Return Patterns
|
|
```javascript
|
|
// Pattern 1: Single object transformation (39% of successful nodes)
|
|
return [{
|
|
json: {
|
|
field1: value1,
|
|
field2: value2
|
|
}
|
|
}];
|
|
|
|
// Pattern 2: Array passthrough (24% of successful nodes)
|
|
return $input.all();
|
|
|
|
// Pattern 3: Mapped transformation (most common)
|
|
const transformed = $input.all()
|
|
.filter(item => item.json.valid)
|
|
.map(item => ({
|
|
json: {
|
|
id: item.json.id,
|
|
processed: true,
|
|
timestamp: new Date().toISOString()
|
|
}
|
|
}));
|
|
return transformed;
|
|
|
|
// Pattern 4: Conditional returns
|
|
if (shouldProcess) {
|
|
return [{json: processedData}];
|
|
} else {
|
|
return []; // Empty array when no data
|
|
}
|
|
|
|
// Pattern 5: Multiple outputs
|
|
const results = [];
|
|
for (const item of $input.all()) {
|
|
if (item.json.valid) {
|
|
results.push({json: item.json});
|
|
}
|
|
}
|
|
return results;
|
|
```
|
|
|
|
### What NOT to Return
|
|
```javascript
|
|
// ❌ Incorrect: Raw data without json wrapper
|
|
return $input.all(); // Missing .map()
|
|
|
|
// ❌ Incorrect: String instead of object
|
|
return "processed";
|
|
|
|
// ❌ Incorrect: Object without array wrapper
|
|
return {
|
|
json: {field: value}
|
|
};
|
|
|
|
// ❌ Incorrect: Incomplete structure
|
|
return [{data: value}]; // Should be: {json: value}
|
|
|
|
// ❌ Incorrect: Throwing without structure
|
|
throw new Error("Something failed"); // No graceful handling
|
|
```
|
|
|
|
---
|
|
|
|
## 4. Top 5 Error Patterns to Avoid
|
|
|
|
### Error #1: Empty Code (23% of validation failures)
|
|
```
|
|
Message: "Code cannot be empty"
|
|
Occurrences: 58
|
|
|
|
Solution: Always include meaningful code or use a different node type
|
|
```
|
|
|
|
**What to Do:**
|
|
```javascript
|
|
// Always provide implementation
|
|
const items = $input.all();
|
|
return items.map(item => ({
|
|
json: {
|
|
...item.json,
|
|
processed: true
|
|
}
|
|
}));
|
|
```
|
|
|
|
---
|
|
|
|
### Error #2: Missing Return Statement (15% of validation failures)
|
|
```
|
|
Message: "Code must return data for the next node"
|
|
Occurrences: 29
|
|
|
|
Solution: Always return data, even if empty
|
|
```
|
|
|
|
**What to Do:**
|
|
```javascript
|
|
const items = $input.all();
|
|
|
|
// Always include a return statement
|
|
if (items.length === 0) {
|
|
return []; // Return empty array if no items
|
|
}
|
|
|
|
return items.map(item => ({json: item.json}));
|
|
```
|
|
|
|
---
|
|
|
|
### Error #3: Expression Syntax Confusion (8% of validation failures)
|
|
```
|
|
Message: "Expression syntax {{...}} is not valid in Code nodes"
|
|
Occurrences: 5
|
|
|
|
Solution: Use JavaScript template literals, NOT n8n expressions
|
|
```
|
|
|
|
**What to Do:**
|
|
```javascript
|
|
// ❌ Wrong: Using n8n expression syntax
|
|
const value = "{{ $json.field }}";
|
|
|
|
// ✅ Correct: Using JavaScript template literals
|
|
const value = `${$json.field}`;
|
|
|
|
// ✅ Also correct: Direct access
|
|
const value = $input.first().json.field;
|
|
```
|
|
|
|
---
|
|
|
|
### Error #4: Unmatched Expression Brackets (6% of validation failures)
|
|
```
|
|
Message: "Unmatched expression brackets: 0 opening, 1 closing"
|
|
Occurrences: 4
|
|
|
|
Solution: Ensure quote/bracket balance in JSONB storage
|
|
```
|
|
|
|
**What to Do:**
|
|
```javascript
|
|
// When storing multi-line strings, escape properly
|
|
const code = `const text = 'It\\'s working correctly';
|
|
const result = text.split('\\n');
|
|
return [{json: {result}}];`;
|
|
|
|
// Test: Check all quotes are properly escaped
|
|
```
|
|
|
|
---
|
|
|
|
### Error #5: Incorrect Return Wrapper (5% of validation failures)
|
|
```
|
|
Message: "Return value must be an array of objects"
|
|
Occurrences: 3
|
|
|
|
Solution: Always wrap output in array, each element must have json property
|
|
```
|
|
|
|
**What to Do:**
|
|
```javascript
|
|
// ❌ Wrong: Single object
|
|
return {
|
|
json: {field: value}
|
|
};
|
|
|
|
// ✅ Correct: Array of objects
|
|
return [{
|
|
json: {field: value}
|
|
}];
|
|
|
|
// ✅ Also correct: Array with multiple items
|
|
return [
|
|
{json: {id: 1, data: 'first'}},
|
|
{json: {id: 2, data: 'second'}}
|
|
];
|
|
```
|
|
|
|
---
|
|
|
|
## 5. Performance & Best Practices
|
|
|
|
### Success Rate Metrics
|
|
- **47.16% of workflows** use CODE nodes
|
|
- **4,461 unique users** creating workflows with CODE nodes
|
|
- **Average patterns:** Most successful nodes combine 2-3 common techniques
|
|
|
|
### Common Node Sequence Patterns
|
|
Most successful workflows follow this pattern:
|
|
1. HTTP Request / Webhook (data ingestion)
|
|
2. CODE node (transformation)
|
|
3. CODE node (normalization/enrichment)
|
|
4. Database write / API output
|
|
|
|
### Optimization Tips
|
|
|
|
**1. Use `$input.all()` over loops when possible:**
|
|
```javascript
|
|
// ❌ Slower: Multiple loops
|
|
let results = [];
|
|
for (const item of $input.all()) {
|
|
results.push({json: item.json});
|
|
}
|
|
|
|
// ✅ Faster: Single map operation
|
|
return $input.all().map(item => ({json: item.json}));
|
|
```
|
|
|
|
**2. Filter early, process late:**
|
|
```javascript
|
|
// ✅ Good: Filter first, then transform
|
|
const processed = $input.all()
|
|
.filter(item => item.json.valid)
|
|
.map(item => ({json: normalize(item.json)}));
|
|
```
|
|
|
|
**3. Pre-compile regex patterns:**
|
|
```javascript
|
|
// ✅ Define outside loop
|
|
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
|
|
for (const item of $input.all()) {
|
|
if (emailRegex.test(item.json.email)) {
|
|
// Process valid email
|
|
}
|
|
}
|
|
```
|
|
|
|
**4. Use guard clauses:**
|
|
```javascript
|
|
// ✅ Fail fast
|
|
if (!$input.first().json.data) {
|
|
return [];
|
|
}
|
|
|
|
const data = $input.first().json.data;
|
|
// Continue processing
|
|
```
|
|
|
|
---
|
|
|
|
## 6. Library & Built-in Availability
|
|
|
|
### n8n Built-in Methods & Variables (JavaScript)
|
|
|
|
#### Core Data Access
|
|
| Method | Description | Example |
|
|
|--------|-------------|---------|
|
|
| `$input.all()` | Get all input items | `const items = $input.all();` |
|
|
| `$input.first()` | Get first input item | `const first = $input.first();` |
|
|
| `$input.last()` | Get last input item | `const last = $input.last();` |
|
|
| `$input.item` | Current item (Each Item mode) | `const current = $input.item;` |
|
|
| `items` | Array of all items (legacy) | `items[0].json.field` |
|
|
| `$json` | Current item JSON (Each Item mode) | `const field = $json.field;` |
|
|
| `$binary` | Current item binary data | `$binary.data` |
|
|
|
|
#### Node & Workflow Context
|
|
| Method | Description | Example |
|
|
|--------|-------------|---------|
|
|
| `$node` | Reference other node outputs | `$node['HTTP Request'].json.data` |
|
|
| `$prevNode` | Access previous node data | `$prevNode.name` |
|
|
| `$workflow` | Workflow metadata | `$workflow.name`, `$workflow.id` |
|
|
| `$execution` | Execution context | `$execution.id`, `$execution.mode` |
|
|
| `$env` | Environment variables | `$env.MY_VAR` |
|
|
|
|
#### Date & Time Helpers (Luxon-based)
|
|
| Variable | Description | Example Output |
|
|
|----------|-------------|----------------|
|
|
| `$now` | Current datetime object | Luxon DateTime |
|
|
| `$today` | Today at midnight | Luxon DateTime |
|
|
| `$now.toISO()` | ISO 8601 format | `"2025-01-20T10:30:00.000Z"` |
|
|
| `$now.toFormat('yyyy-MM-dd')` | Custom format | `"2025-01-20"` |
|
|
| `$now.plus({days: 7})` | Date arithmetic | 7 days from now |
|
|
| `$now.minus({hours: 2})` | Subtract time | 2 hours ago |
|
|
|
|
```javascript
|
|
// Date examples
|
|
const tomorrow = $now.plus({days: 1}).toISO();
|
|
const lastWeek = $now.minus({weeks: 1}).toFormat('yyyy-MM-dd');
|
|
const isWeekend = $now.weekday > 5; // 6 = Saturday, 7 = Sunday
|
|
```
|
|
|
|
#### Data Querying with JMESPath
|
|
| Method | Description | Example |
|
|
|--------|-------------|---------|
|
|
| `$jmespath(data, query)` | Query JSON structures | `$jmespath(data, 'users[?age > `21`].name')` |
|
|
|
|
```javascript
|
|
// JMESPath examples
|
|
const data = $input.first().json;
|
|
|
|
// Filter array
|
|
const adults = $jmespath(data, 'users[?age >= `18`]');
|
|
|
|
// Extract specific fields
|
|
const names = $jmespath(data, 'users[*].name');
|
|
|
|
// Complex queries
|
|
const topScores = $jmespath(data, 'scores | sort_by(@, &value) | reverse(@) | [0:5]');
|
|
```
|
|
|
|
#### Utility Methods
|
|
| Method | Description | Example |
|
|
|--------|-------------|---------|
|
|
| `$getWorkflowStaticData()` | Persistent workflow data | `const counter = $getWorkflowStaticData().counter \|\| 0;` |
|
|
| `$evaluateExpression(expr, itemIndex)` | Evaluate n8n expression | `$evaluateExpression('{{ $json.field }}', 0)` |
|
|
|
|
### Python Built-in Methods (Beta)
|
|
|
|
| Python | JavaScript | Description |
|
|
|--------|------------|-------------|
|
|
| `_input.all()` | `$input.all()` | Get all items |
|
|
| `_input.first()` | `$input.first()` | Get first item |
|
|
| `_input.last()` | `$input.last()` | Get last item |
|
|
| `_input.item` | `$input.item` | Current item |
|
|
| `_items` | `items` | All items array (Native) |
|
|
| `_item` | `$item` | Current item (Native) |
|
|
| `_now` | `$now` | Current datetime |
|
|
| `_today` | `$today` | Today at midnight |
|
|
| `_jmespath(data, query)` | `$jmespath()` | Query JSON |
|
|
|
|
```python
|
|
# Python (Beta) examples
|
|
from datetime import timedelta
|
|
|
|
# Date operations
|
|
tomorrow = _now + timedelta(days=1)
|
|
last_week = _now - timedelta(weeks=1)
|
|
|
|
# JMESPath querying
|
|
data = _input.first()["json"]
|
|
adults = _jmespath(data, 'users[?age >= `18`]')
|
|
```
|
|
|
|
### Standard JavaScript/Python Objects (No imports needed)
|
|
|
|
**JavaScript:**
|
|
- `Math` - Math functions: `Math.max()`, `Math.random()`, etc.
|
|
- `Date` - Date operations: `new Date()`, `.toISOString()`
|
|
- `JSON` - JSON parsing: `JSON.parse()`, `JSON.stringify()`
|
|
- `Buffer` - Base64: `Buffer.from(data, 'base64')`
|
|
- `console` - Logging: `console.log()`, `console.error()`
|
|
- `Object` - Object methods: `Object.keys()`, `Object.entries()`
|
|
- `Array` - Array methods: `.map()`, `.filter()`, `.reduce()`
|
|
|
|
**Python:**
|
|
- `re` - Regular expressions
|
|
- `json` - JSON parsing
|
|
- `datetime` - Date/time operations
|
|
- `statistics` - Statistical functions
|
|
- `base64` - Base64 encoding/decoding
|
|
- `print()` - Debug logging
|
|
|
|
### Common Code Patterns
|
|
|
|
**Base64 Encoding/Decoding:**
|
|
```javascript
|
|
// Decode
|
|
const decoded = Buffer.from(encoded, 'base64').toString();
|
|
|
|
// Encode
|
|
const encoded = Buffer.from(text).toString('base64');
|
|
```
|
|
|
|
**Date Formatting:**
|
|
```javascript
|
|
// ISO format
|
|
const iso = new Date().toISOString();
|
|
|
|
// Locale string
|
|
const local = new Date().toLocaleString('en-US');
|
|
|
|
// Custom format
|
|
const parts = new Date().toISOString().split('T');
|
|
const date = parts[0]; // YYYY-MM-DD
|
|
```
|
|
|
|
**JSON Operations:**
|
|
```javascript
|
|
// Parse with default
|
|
const data = JSON.parse(jsonString || '{}');
|
|
|
|
// Stringify with formatting
|
|
const pretty = JSON.stringify(data, null, 2);
|
|
|
|
// Ordered keys
|
|
const ordered = {};
|
|
Object.keys(data).sort().forEach(key => {
|
|
ordered[key] = data[key];
|
|
});
|
|
```
|
|
|
|
---
|
|
|
|
## 7. Real-World Template Examples
|
|
|
|
The following examples are from popular n8n workflow templates (214K+ views):
|
|
|
|
### Example 1: Scene Extraction with Error Handling
|
|
```javascript
|
|
// From: "Generate AI Viral Videos" workflow (214,907 views)
|
|
function findSceneEntries(obj) {
|
|
const scenes = [];
|
|
|
|
for (const [key, value] of Object.entries(obj)) {
|
|
if (key.toLowerCase().startsWith("scene") && typeof value === "string") {
|
|
scenes.push(value);
|
|
} else if (typeof value === "object" && value !== null) {
|
|
scenes.push(...findSceneEntries(value)); // Recursive search
|
|
}
|
|
}
|
|
|
|
return scenes;
|
|
}
|
|
|
|
let output = [];
|
|
|
|
try {
|
|
const inputData = items[0].json;
|
|
const scenes = findSceneEntries(inputData);
|
|
|
|
if (scenes.length === 0) {
|
|
throw new Error("No scene keys found at any level.");
|
|
}
|
|
|
|
output = scenes.map(scene => ({ description: scene }));
|
|
} catch (e) {
|
|
throw new Error("Could not extract scenes properly. Details: " + e.message);
|
|
}
|
|
|
|
return output;
|
|
```
|
|
**Key Techniques:** Recursive object traversal, try-catch error handling, validation
|
|
|
|
### Example 2: Array to Object Property Mapping
|
|
```javascript
|
|
// From: "Generate AI Viral Videos" workflow (214,907 views)
|
|
// Collect video URLs from multiple items into single object
|
|
return [
|
|
{
|
|
video_urls: items.map(item => item.json.video.url)
|
|
}
|
|
];
|
|
```
|
|
**Key Techniques:** Array mapping, property extraction, single result aggregation
|
|
|
|
### Example 3: Binary Data Manipulation
|
|
```javascript
|
|
// From: "Automate Social Media Content" workflow (205,470 views)
|
|
// Rename binary property for downstream processing
|
|
$input.first().binary.data = $input.first().binary.Upload_Image__optional_
|
|
delete $input.first().binary.Upload_Image__optional_
|
|
return $input.first()
|
|
```
|
|
**Key Techniques:** Binary property manipulation, object mutation, passthrough pattern
|
|
|
|
---
|
|
|
|
## 8. Quick Reference Checklist
|
|
|
|
Before deploying CODE nodes, verify:
|
|
|
|
- [ ] **Code is not empty** - Must have meaningful logic
|
|
- [ ] **Return statement exists** - Must return array of objects
|
|
- [ ] **Proper return format** - Each item: `{json: {...}}`
|
|
- [ ] **Data access correct** - Using `$input.all()`, `$input.first()`, or `$input.item`
|
|
- [ ] **No n8n expressions** - Use JavaScript template literals instead: `` `${value}` ``
|
|
- [ ] **Error handling** - Guard clauses for null/undefined inputs
|
|
- [ ] **Quote escaping** - Properly escape strings in JSONB
|
|
- [ ] **Loop logic correct** - Avoid infinite loops, use proper conditions
|
|
- [ ] **Performance** - Prefer map/filter over manual loops for small datasets
|
|
- [ ] **Output consistent** - All paths return same structure
|
|
|
|
---
|
|
|
|
## 9. Additional Resources
|
|
|
|
### Official n8n Documentation
|
|
- **Code Node Guide:** https://docs.n8n.io/code/code-node/
|
|
- **Code Examples:** https://docs.n8n.io/code/cookbook/code-node/
|
|
- **Built-in Methods Reference:** https://docs.n8n.io/code-examples/methods-variables-reference/
|
|
- **n8n Expressions:** https://docs.n8n.io/code/expressions/
|
|
- **Luxon Date Library:** https://moment.github.io/luxon/
|
|
|
|
### Common Use Cases Quick Reference
|
|
- **Data transformation:** See Section 2, Patterns 1, 3, 5
|
|
- **Filtering & ranking:** See Section 2, Patterns 2, 6, 9
|
|
- **Format conversion:** See Section 2, Patterns 4, 7, 8
|
|
- **Python examples:** See Section 2 (Python patterns)
|
|
- **Error handling:** See Section 5 (Performance tips, guard clauses)
|
|
- **Real-world examples:** See Section 7 (Template examples)
|
|
|
|
### When to Use CODE Node vs Other Nodes
|
|
| Scenario | Use This | Not CODE Node |
|
|
|----------|----------|---------------|
|
|
| Simple field mapping | Set node | ✓ Simpler UI |
|
|
| Basic filtering | Filter node | ✓ Visual interface |
|
|
| Conditional routing | If/Switch node | ✓ Better clarity |
|
|
| Complex transformations | **CODE node** | ✗ Too limited |
|
|
| Multi-step logic | **CODE node** | ✗ Needs chaining |
|
|
| Custom calculations | **CODE node** | ✗ No built-in |
|
|
| API response parsing | **CODE node** | ✗ Complex structure |
|
|
| Recursive operations | **CODE node** | ✗ Not possible |
|
|
|
|
### Related n8n Nodes
|
|
- **If/Switch:** Conditional logic (use CODE for complex conditions with multiple criteria)
|
|
- **Set:** Simple field mapping (use CODE for transformations requiring logic)
|
|
- **Merge:** Combining data (use CODE for custom merge logic with conflict resolution)
|
|
- **Split:** Array handling (use CODE for complex filtering and grouping)
|
|
- **Function:** Legacy node (CODE node is the modern replacement)
|
|
- **Execute Command:** Shell commands (use CODE for JavaScript/Python processing)
|
|
|
|
### n8n Community Resources
|
|
- **Community Forum:** https://community.n8n.io/c/questions/code-node
|
|
- **Workflow Templates:** https://n8n.io/workflows (filter by "Code" node)
|
|
- **GitHub Discussions:** https://github.com/n8n-io/n8n/discussions
|
|
|
|
---
|
|
|
|
## 10. Summary & Key Takeaways
|
|
|
|
### Essential Rules for CODE Node Success
|
|
|
|
1. **Choose the Right Mode**
|
|
- Use "Run Once for All Items" (default) for 95% of use cases
|
|
- Only use "Each Item" mode for independent per-item operations
|
|
|
|
2. **Master Data Access Patterns**
|
|
- `$input.all()` for batch processing and aggregation (26% usage)
|
|
- `$input.first()` for single-item operations (25% usage)
|
|
- `$input.item` only in "Each Item" mode (19% usage)
|
|
|
|
3. **Always Return Correct Format**
|
|
- Single result: `return [{json: {...}}]`
|
|
- Multiple results: `return items.map(item => ({json: item}))`
|
|
- No results: `return []`
|
|
- Never return raw objects, strings, or missing array wrapper
|
|
|
|
4. **Use JavaScript Unless You Need Python**
|
|
- JavaScript: 95% of use cases, faster, full n8n helpers
|
|
- Python: Data science, ML, specific library requirements only
|
|
|
|
5. **Implement Error Handling**
|
|
- Use guard clauses for null/undefined checks
|
|
- Provide fallback values with `.get()` or `||` operator
|
|
- Wrap risky operations in try-catch blocks
|
|
- Return meaningful error messages
|
|
|
|
6. **Optimize for Performance**
|
|
- Prefer `.map()`, `.filter()`, `.reduce()` over manual loops
|
|
- Filter early, process late
|
|
- Pre-compile regex patterns outside loops
|
|
- Use early returns to avoid unnecessary processing
|
|
|
|
7. **Debug Effectively**
|
|
- JavaScript: `console.log()` outputs to browser console
|
|
- Python: `print()` statements for debugging
|
|
- Test with minimal data first, then scale up
|
|
- Validate with n8n's built-in execution viewer
|
|
|
|
### Common Pitfalls to Avoid
|
|
|
|
❌ **Empty code or missing return statement** (39% of failures)
|
|
❌ **Using n8n expression syntax `{{}}` instead of JavaScript template literals**
|
|
❌ **Returning raw objects without `[{json: {...}}]` wrapper**
|
|
❌ **Accessing properties without null checks** (causes crashes)
|
|
❌ **Using wrong mode** (Each Item when All Items would be better)
|
|
|
|
### Success Metrics from Real-World Data
|
|
|
|
- **47.16% of all n8n workflows** use CODE nodes (15,202 workflows analyzed)
|
|
- **4,461 unique users** creating CODE node workflows
|
|
- **78% use "All Items" mode** (more efficient for batch operations)
|
|
- **39% success rate improvement** when following return format patterns
|
|
|
|
### Next Steps
|
|
|
|
1. **Start Simple:** Begin with basic transformations using the patterns in Section 2
|
|
2. **Study Examples:** Review the 10 successful patterns and template examples (Sections 2.1-2.10, 7)
|
|
3. **Test Incrementally:** Start with 1-2 items, then scale to production data
|
|
4. **Use the Checklist:** Follow Section 8 before deploying to production
|
|
5. **Learn n8n Helpers:** Master `$input`, `$now`, and `$jmespath` (Section 6)
|
|
6. **Join the Community:** Ask questions on the n8n forum for specific use cases
|
|
|
|
With these patterns, best practices, and real-world insights, you'll create robust, maintainable CODE nodes that process data efficiently and reliably across your n8n workflows.
|
|
|
|
---
|
|
|
|
**Document Metadata:**
|
|
- **Based on:** 38,094 CODE node instances from 15,202 workflows
|
|
- **Analysis Period:** Last 60 days
|
|
- **Data Sources:** n8n telemetry database, workflow templates, official documentation
|
|
- **Last Updated:** January 2025
|
|
- **n8n Version:** Supports Code node v2.x (JavaScript, Python Beta, Python Native Beta)
|
|
|
|
---
|
|
|
|
**Conceived by Romuald Członkowski** - [www.aiadvisors.pl/en](https://www.aiadvisors.pl/en)
|