
N8n Code Javascript
- 7 installs
- 82 repo stars
- Updated August 2, 2026
- aaaaqwq/claude-code-skills
n8n-code-javascript is a Claude Code skill that guides writing JavaScript in n8n Code nodes.
About
n8n-code-javascript is a Claude Code skill that guides writing JavaScript inside n8n Code nodes. It covers the two execution modes, data-access patterns with $input, $json and $node, the required array-of-json return format, and common errors like webhook payloads nested under .body. A developer uses it when building or debugging JavaScript logic in an n8n workflow.
- Reference for writing JavaScript in n8n Code nodes using $input/$json/$node syntax
- Explains the two Code node modes and when to use each
- Flags common mistakes like webhook data nested under .body and required return format
N8n Code Javascript by the numbers
- 7 all-time installs (skills.sh)
- Ranked #1,590 of 2,719 Automation & Workflows skills by installs in the Skillselion catalog
- Data as of Aug 3, 2026 (Skillselion catalog sync)
n8n-code-javascript capabilities & compatibility
- Capabilities
- workflow automation · code generation · data transformation
- Works with
- n8n
What n8n-code-javascript says it does
**MOST COMMON MISTAKE**: Webhook data is nested under `.body`
Choose "Run Once for All Items" mode
npx skills add https://github.com/aaaaqwq/claude-code-skills --skill n8n-code-javascriptAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 7 |
|---|---|
| repo stars | ★ 82 |
| Last updated | August 2, 2026 |
| Repository | aaaaqwq/claude-code-skills ↗ |
What it does
Write and debug JavaScript inside n8n Code nodes with correct data access and return formats.
When should I use this skill?
The user writes JavaScript in an n8n Code node or hits a Code node error.
By the numbers
- 4 documented data-access patterns
- 2 Code node execution modes
Files
JavaScript Code Node
Expert guidance for writing JavaScript code in n8n Code nodes.
---
Quick Start
// Basic template for Code nodes
const items = $input.all();
// Process data
const processed = items.map(item => ({
json: {
...item.json,
processed: true,
timestamp: new Date().toISOString()
}
}));
return processed;Essential Rules
1. Choose "Run Once for All Items" mode (recommended for most use cases) 2. Access data: $input.all(), $input.first(), or $input.item 3. CRITICAL: Must return [{json: {...}}] format 4. CRITICAL: Webhook data is under $json.body (not $json directly) 5. Built-ins available: $helpers.httpRequest(), DateTime (Luxon), $jmespath()
---
Mode Selection Guide
The Code node offers two execution modes. Choose based on your use case:
Run Once for All Items (Recommended - Default)
Use this mode for: 95% of use cases
- How it works: Code executes once regardless of input count
- Data access:
$input.all()oritemsarray - Best for: Aggregation, filtering, batch processing, transformations, API calls with all data
- Performance: Faster for multiple items (single execution)
// Example: Calculate total from all items
const allItems = $input.all();
const total = allItems.reduce((sum, item) => sum + (item.json.amount || 0), 0);
return [{
json: {
total,
count: allItems.length,
average: total / allItems.length
}
}];When to use:
- ✅ Comparing items across the dataset
- ✅ Calculating totals, averages, or statistics
- ✅ Sorting or ranking items
- ✅ Deduplication
- ✅ Building aggregated reports
- ✅ Combining data from multiple items
Run Once for Each Item
Use this mode for: Specialized cases only
- How it works: Code executes separately for each input item
- Data access:
$input.itemor$item - Best for: Item-specific logic, independent operations, per-item validation
- Performance: Slower for large datasets (multiple executions)
// Example: Add processing timestamp to each item
const item = $input.item;
return [{
json: {
...item.json,
processed: true,
processedAt: new Date().toISOString()
}
}];When to use:
- ✅ Each item needs independent API call
- ✅ Per-item validation with different error handling
- ✅ Item-specific transformations based on item properties
- ✅ When items must be processed separately for business logic
Decision Shortcut:
- Need to look at multiple items? → Use "All Items" mode
- Each item completely independent? → Use "Each Item" mode
- Not sure? → Use "All Items" mode (you can always loop inside)
---
Data Access Patterns
Pattern 1: $input.all() - Most Common
Use when: Processing arrays, batch operations, aggregations
// Get all items from previous node
const allItems = $input.all();
// Filter, map, reduce as needed
const valid = allItems.filter(item => item.json.status === 'active');
const mapped = valid.map(item => ({
json: {
id: item.json.id,
name: item.json.name
}
}));
return mapped;Pattern 2: $input.first() - Very Common
Use when: Working with single objects, API responses, first-in-first-out
// Get first item only
const firstItem = $input.first();
const data = firstItem.json;
return [{
json: {
result: processData(data),
processedAt: new Date().toISOString()
}
}];Pattern 3: $input.item - Each Item Mode Only
Use when: In "Run Once for Each Item" mode
// Current item in loop (Each Item mode only)
const currentItem = $input.item;
return [{
json: {
...currentItem.json,
itemProcessed: true
}
}];Pattern 4: $node - Reference Other Nodes
Use when: Need data from specific nodes in workflow
// Get output from specific node
const webhookData = $node["Webhook"].json;
const httpData = $node["HTTP Request"].json;
return [{
json: {
combined: {
webhook: webhookData,
api: httpData
}
}
}];See: DATA_ACCESS.md for comprehensive guide
---
Critical: Webhook Data Structure
MOST COMMON MISTAKE: Webhook data is nested under .body
// ❌ WRONG - Will return undefined
const name = $json.name;
const email = $json.email;
// ✅ CORRECT - Webhook data is under .body
const name = $json.body.name;
const email = $json.body.email;
// Or with $input
const webhookData = $input.first().json.body;
const name = webhookData.name;Why: Webhook node wraps all request data under body property. This includes POST data, query parameters, and JSON payloads.
See: DATA_ACCESS.md for full webhook structure details
---
Return Format Requirements
CRITICAL RULE: Always return array of objects with json property
Correct Return Formats
// ✅ Single result
return [{
json: {
field1: value1,
field2: value2
}
}];
// ✅ Multiple results
return [
{json: {id: 1, data: 'first'}},
{json: {id: 2, data: 'second'}}
];
// ✅ Transformed array
const transformed = $input.all()
.filter(item => item.json.valid)
.map(item => ({
json: {
id: item.json.id,
processed: true
}
}));
return transformed;
// ✅ Empty result (when no data to return)
return [];
// ✅ Conditional return
if (shouldProcess) {
return [{json: processedData}];
} else {
return [];
}Incorrect Return Formats
// ❌ WRONG: Object without array wrapper
return {
json: {field: value}
};
// ❌ WRONG: Array without json wrapper
return [{field: value}];
// ❌ WRONG: Plain string
return "processed";
// ❌ WRONG: Raw data without mapping
return $input.all(); // Missing .map()
// ❌ WRONG: Incomplete structure
return [{data: value}]; // Should be {json: value}Why it matters: Next nodes expect array format. Incorrect format causes workflow execution to fail.
See: ERROR_PATTERNS.md #3 for detailed error solutions
---
Common Patterns Overview
Based on production workflows, here are the most useful patterns:
1. Multi-Source Data Aggregation
Combine data from multiple APIs, webhooks, or nodes
const allItems = $input.all();
const results = [];
for (const item of allItems) {
const sourceName = item.json.name || 'Unknown';
// Parse source-specific structure
if (sourceName === 'API1' && item.json.data) {
results.push({
json: {
title: item.json.data.title,
source: 'API1'
}
});
}
}
return results;2. Filtering with Regex
Extract patterns, mentions, or keywords from text
const pattern = /\b([A-Z]{2,5})\b/g;
const matches = {};
for (const item of $input.all()) {
const text = item.json.text;
const found = text.match(pattern);
if (found) {
found.forEach(match => {
matches[match] = (matches[match] || 0) + 1;
});
}
}
return [{json: {matches}}];3. Data Transformation & Enrichment
Map fields, normalize formats, add computed fields
const items = $input.all();
return items.map(item => {
const data = item.json;
const nameParts = data.name.split(' ');
return {
json: {
first_name: nameParts[0],
last_name: nameParts.slice(1).join(' '),
email: data.email,
created_at: new Date().toISOString()
}
};
});4. Top N Filtering & Ranking
Sort and limit results
const items = $input.all();
const topItems = items
.sort((a, b) => (b.json.score || 0) - (a.json.score || 0))
.slice(0, 10);
return topItems.map(item => ({json: item.json}));5. Aggregation & Reporting
Sum, count, group data
const items = $input.all();
const total = items.reduce((sum, item) => sum + (item.json.amount || 0), 0);
return [{
json: {
total,
count: items.length,
average: total / items.length,
timestamp: new Date().toISOString()
}
}];See: COMMON_PATTERNS.md for 10 detailed production patterns
---
Error Prevention - Top 5 Mistakes
#1: Empty Code or Missing Return (Most Common)
// ❌ WRONG: No return statement
const items = $input.all();
// ... processing code ...
// Forgot to return!
// ✅ CORRECT: Always return data
const items = $input.all();
// ... processing ...
return items.map(item => ({json: item.json}));#2: Expression Syntax Confusion
// ❌ WRONG: Using n8n expression syntax in code
const value = "{{ $json.field }}";
// ✅ CORRECT: Use JavaScript template literals
const value = `${$json.field}`;
// ✅ CORRECT: Direct access
const value = $input.first().json.field;#3: Incorrect Return Wrapper
// ❌ WRONG: Returning object instead of array
return {json: {result: 'success'}};
// ✅ CORRECT: Array wrapper required
return [{json: {result: 'success'}}];#4: Missing Null Checks
// ❌ WRONG: Crashes if field doesn't exist
const value = item.json.user.email;
// ✅ CORRECT: Safe access with optional chaining
const value = item.json?.user?.email || 'no-email@example.com';
// ✅ CORRECT: Guard clause
if (!item.json.user) {
return [];
}
const value = item.json.user.email;#5: Webhook Body Nesting
// ❌ WRONG: Direct access to webhook data
const email = $json.email;
// ✅ CORRECT: Webhook data under .body
const email = $json.body.email;See: ERROR_PATTERNS.md for comprehensive error guide
---
Built-in Functions & Helpers
$helpers.httpRequest()
Make HTTP requests from within code:
const response = await $helpers.httpRequest({
method: 'GET',
url: 'https://api.example.com/data',
headers: {
'Authorization': 'Bearer token',
'Content-Type': 'application/json'
}
});
return [{json: {data: response}}];DateTime (Luxon)
Date and time operations:
// Current time
const now = DateTime.now();
// Format dates
const formatted = now.toFormat('yyyy-MM-dd');
const iso = now.toISO();
// Date arithmetic
const tomorrow = now.plus({days: 1});
const lastWeek = now.minus({weeks: 1});
return [{
json: {
today: formatted,
tomorrow: tomorrow.toFormat('yyyy-MM-dd')
}
}];$jmespath()
Query JSON structures:
const data = $input.first().json;
// Filter array
const adults = $jmespath(data, 'users[?age >= `18`]');
// Extract fields
const names = $jmespath(data, 'users[*].name');
return [{json: {adults, names}}];See: BUILTIN_FUNCTIONS.md for complete reference
---
Best Practices
1. Always Validate Input Data
const items = $input.all();
// Check if data exists
if (!items || items.length === 0) {
return [];
}
// Validate structure
if (!items[0].json) {
return [{json: {error: 'Invalid input format'}}];
}
// Continue processing...2. Use Try-Catch for Error Handling
try {
const response = await $helpers.httpRequest({
url: 'https://api.example.com/data'
});
return [{json: {success: true, data: response}}];
} catch (error) {
return [{
json: {
success: false,
error: error.message
}
}];
}3. Prefer Array Methods Over Loops
// ✅ GOOD: Functional approach
const processed = $input.all()
.filter(item => item.json.valid)
.map(item => ({json: {id: item.json.id}}));
// ❌ SLOWER: Manual loop
const processed = [];
for (const item of $input.all()) {
if (item.json.valid) {
processed.push({json: {id: item.json.id}});
}
}4. Filter Early, Process Late
// ✅ GOOD: Filter first to reduce processing
const processed = $input.all()
.filter(item => item.json.status === 'active') // Reduce dataset first
.map(item => expensiveTransformation(item)); // Then transform
// ❌ WASTEFUL: Transform everything, then filter
const processed = $input.all()
.map(item => expensiveTransformation(item)) // Wastes CPU
.filter(item => item.json.status === 'active');5. Use Descriptive Variable Names
// ✅ GOOD: Clear intent
const activeUsers = $input.all().filter(item => item.json.active);
const totalRevenue = activeUsers.reduce((sum, user) => sum + user.json.revenue, 0);
// ❌ BAD: Unclear purpose
const a = $input.all().filter(item => item.json.active);
const t = a.reduce((s, u) => s + u.json.revenue, 0);6. Debug with console.log()
// Debug statements appear in browser console
const items = $input.all();
console.log(`Processing ${items.length} items`);
for (const item of items) {
console.log('Item data:', item.json);
// Process...
}
return result;---
When to Use Code Node
Use Code node when:
- ✅ Complex transformations requiring multiple steps
- ✅ Custom calculations or business logic
- ✅ Recursive operations
- ✅ API response parsing with complex structure
- ✅ Multi-step conditionals
- ✅ Data aggregation across items
Consider other nodes when:
- ❌ Simple field mapping → Use Set node
- ❌ Basic filtering → Use Filter node
- ❌ Simple conditionals → Use IF or Switch node
- ❌ HTTP requests only → Use HTTP Request node
Code node excels at: Complex logic that would require chaining many simple nodes
---
Integration with Other Skills
Works With:
n8n Expression Syntax:
- Expressions use
{{ }}syntax in other nodes - Code nodes use JavaScript directly (no
{{ }}) - When to use expressions vs code
n8n MCP Tools Expert:
- How to find Code node:
search_nodes({query: "code"}) - Get configuration help:
get_node_essentials("nodes-base.code") - Validate code:
validate_node_operation()
n8n Node Configuration:
- Mode selection (All Items vs Each Item)
- Language selection (JavaScript vs Python)
- Understanding property dependencies
n8n Workflow Patterns:
- Code nodes in transformation step
- Webhook → Code → API pattern
- Error handling in workflows
n8n Validation Expert:
- Validate Code node configuration
- Handle validation errors
- Auto-fix common issues
---
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: `
${value}` - [ ] Error handling - Guard clauses for null/undefined inputs
- [ ] Webhook data - Access via
.bodyif from webhook - [ ] Mode selection - "All Items" for most cases
- [ ] Performance - Prefer map/filter over manual loops
- [ ] Output consistent - All code paths return same structure
---
Additional Resources
Related Files
- DATA_ACCESS.md - Comprehensive data access patterns
- COMMON_PATTERNS.md - 10 production-tested patterns
- ERROR_PATTERNS.md - Top 5 errors and solutions
- BUILTIN_FUNCTIONS.md - Complete built-in reference
n8n Documentation
- Code Node Guide: https://docs.n8n.io/code/code-node/
- Built-in Methods: https://docs.n8n.io/code-examples/methods-variables-reference/
- Luxon Documentation: https://moment.github.io/luxon/
---
Ready to write JavaScript in n8n Code nodes! Start with simple transformations, use the error patterns guide to avoid common mistakes, and reference the pattern library for production-ready examples.
Built-in Functions - JavaScript Code Node
Complete reference for n8n's built-in JavaScript functions and helpers.
---
Overview
n8n Code nodes provide powerful built-in functions beyond standard JavaScript. This guide covers:
1. $helpers.httpRequest() - Make HTTP requests 2. DateTime (Luxon) - Advanced date/time operations 3. $jmespath() - Query JSON structures 4. $getWorkflowStaticData() - Persistent storage 5. Standard JavaScript Globals - Math, JSON, console, etc. 6. Available Node.js Modules - crypto, Buffer, URL
---
1. $helpers.httpRequest() - HTTP Requests
Make HTTP requests directly from Code nodes without using HTTP Request node.
Basic Usage
const response = await $helpers.httpRequest({
method: 'GET',
url: 'https://api.example.com/users'
});
return [{json: {data: response}}];Complete Options
const response = await $helpers.httpRequest({
method: 'POST', // GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS
url: 'https://api.example.com/users',
headers: {
'Authorization': 'Bearer token123',
'Content-Type': 'application/json',
'User-Agent': 'n8n-workflow'
},
body: {
name: 'John Doe',
email: 'john@example.com'
},
qs: { // Query string parameters
page: 1,
limit: 10
},
timeout: 10000, // Milliseconds (default: no timeout)
json: true, // Auto-parse JSON response (default: true)
simple: false, // Don't throw on HTTP errors (default: true)
resolveWithFullResponse: false // Return only body (default: false)
});GET Request
// Simple GET
const users = await $helpers.httpRequest({
method: 'GET',
url: 'https://api.example.com/users'
});
return [{json: {users}}];// GET with query parameters
const results = await $helpers.httpRequest({
method: 'GET',
url: 'https://api.example.com/search',
qs: {
q: 'javascript',
page: 1,
per_page: 50
}
});
return [{json: results}];POST Request
// POST with JSON body
const newUser = await $helpers.httpRequest({
method: 'POST',
url: 'https://api.example.com/users',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + $env.API_KEY
},
body: {
name: $json.body.name,
email: $json.body.email,
role: 'user'
}
});
return [{json: newUser}];PUT/PATCH Request
// Update resource
const updated = await $helpers.httpRequest({
method: 'PATCH',
url: `https://api.example.com/users/${userId}`,
body: {
name: 'Updated Name',
status: 'active'
}
});
return [{json: updated}];DELETE Request
// Delete resource
await $helpers.httpRequest({
method: 'DELETE',
url: `https://api.example.com/users/${userId}`,
headers: {
'Authorization': 'Bearer ' + $env.API_KEY
}
});
return [{json: {deleted: true, userId}}];Authentication Patterns
// Bearer Token
const response = await $helpers.httpRequest({
url: 'https://api.example.com/data',
headers: {
'Authorization': `Bearer ${$env.API_TOKEN}`
}
});// API Key in Header
const response = await $helpers.httpRequest({
url: 'https://api.example.com/data',
headers: {
'X-API-Key': $env.API_KEY
}
});// Basic Auth (manual)
const credentials = Buffer.from(`${username}:${password}`).toString('base64');
const response = await $helpers.httpRequest({
url: 'https://api.example.com/data',
headers: {
'Authorization': `Basic ${credentials}`
}
});Error Handling
// Handle HTTP errors gracefully
try {
const response = await $helpers.httpRequest({
method: 'GET',
url: 'https://api.example.com/users',
simple: false // Don't throw on 4xx/5xx
});
if (response.statusCode >= 200 && response.statusCode < 300) {
return [{json: {success: true, data: response.body}}];
} else {
return [{
json: {
success: false,
status: response.statusCode,
error: response.body
}
}];
}
} catch (error) {
return [{
json: {
success: false,
error: error.message
}
}];
}Full Response Access
// Get full response including headers and status
const response = await $helpers.httpRequest({
url: 'https://api.example.com/data',
resolveWithFullResponse: true
});
return [{
json: {
statusCode: response.statusCode,
headers: response.headers,
body: response.body,
rateLimit: response.headers['x-ratelimit-remaining']
}
}];---
2. DateTime (Luxon) - Date & Time Operations
n8n includes Luxon for powerful date/time handling. Access via DateTime global.
Current Date/Time
// Current time
const now = DateTime.now();
// Current time in specific timezone
const nowTokyo = DateTime.now().setZone('Asia/Tokyo');
// Today at midnight
const today = DateTime.now().startOf('day');
return [{
json: {
iso: now.toISO(), // "2025-01-20T15:30:00.000Z"
formatted: now.toFormat('yyyy-MM-dd HH:mm:ss'), // "2025-01-20 15:30:00"
unix: now.toSeconds(), // Unix timestamp
millis: now.toMillis() // Milliseconds since epoch
}
}];Formatting Dates
const now = DateTime.now();
return [{
json: {
isoFormat: now.toISO(), // ISO 8601: "2025-01-20T15:30:00.000Z"
sqlFormat: now.toSQL(), // SQL: "2025-01-20 15:30:00.000"
httpFormat: now.toHTTP(), // HTTP: "Mon, 20 Jan 2025 15:30:00 GMT"
// Custom formats
dateOnly: now.toFormat('yyyy-MM-dd'), // "2025-01-20"
timeOnly: now.toFormat('HH:mm:ss'), // "15:30:00"
readable: now.toFormat('MMMM dd, yyyy'), // "January 20, 2025"
compact: now.toFormat('yyyyMMdd'), // "20250120"
withDay: now.toFormat('EEEE, MMMM dd, yyyy'), // "Monday, January 20, 2025"
custom: now.toFormat('dd/MM/yy HH:mm') // "20/01/25 15:30"
}
}];Parsing Dates
// From ISO string
const dt1 = DateTime.fromISO('2025-01-20T15:30:00');
// From specific format
const dt2 = DateTime.fromFormat('01/20/2025', 'MM/dd/yyyy');
// From SQL
const dt3 = DateTime.fromSQL('2025-01-20 15:30:00');
// From Unix timestamp
const dt4 = DateTime.fromSeconds(1737384600);
// From milliseconds
const dt5 = DateTime.fromMillis(1737384600000);
return [{json: {parsed: dt1.toISO()}}];Date Arithmetic
const now = DateTime.now();
return [{
json: {
// Adding time
tomorrow: now.plus({days: 1}).toISO(),
nextWeek: now.plus({weeks: 1}).toISO(),
nextMonth: now.plus({months: 1}).toISO(),
inTwoHours: now.plus({hours: 2}).toISO(),
// Subtracting time
yesterday: now.minus({days: 1}).toISO(),
lastWeek: now.minus({weeks: 1}).toISO(),
lastMonth: now.minus({months: 1}).toISO(),
twoHoursAgo: now.minus({hours: 2}).toISO(),
// Complex operations
in90Days: now.plus({days: 90}).toFormat('yyyy-MM-dd'),
in6Months: now.plus({months: 6}).toFormat('yyyy-MM-dd')
}
}];Time Comparisons
const now = DateTime.now();
const targetDate = DateTime.fromISO('2025-12-31');
return [{
json: {
// Comparisons
isFuture: targetDate > now,
isPast: targetDate < now,
isEqual: targetDate.equals(now),
// Differences
daysUntil: targetDate.diff(now, 'days').days,
hoursUntil: targetDate.diff(now, 'hours').hours,
monthsUntil: targetDate.diff(now, 'months').months,
// Detailed difference
detailedDiff: targetDate.diff(now, ['months', 'days', 'hours']).toObject()
}
}];Timezone Operations
const now = DateTime.now();
return [{
json: {
// Current timezone
local: now.toISO(),
// Convert to different timezone
tokyo: now.setZone('Asia/Tokyo').toISO(),
newYork: now.setZone('America/New_York').toISO(),
london: now.setZone('Europe/London').toISO(),
utc: now.toUTC().toISO(),
// Get timezone info
timezone: now.zoneName, // "America/Los_Angeles"
offset: now.offset, // Offset in minutes
offsetFormatted: now.toFormat('ZZ') // "+08:00"
}
}];Start/End of Period
const now = DateTime.now();
return [{
json: {
startOfDay: now.startOf('day').toISO(),
endOfDay: now.endOf('day').toISO(),
startOfWeek: now.startOf('week').toISO(),
endOfWeek: now.endOf('week').toISO(),
startOfMonth: now.startOf('month').toISO(),
endOfMonth: now.endOf('month').toISO(),
startOfYear: now.startOf('year').toISO(),
endOfYear: now.endOf('year').toISO()
}
}];Weekday & Month Info
const now = DateTime.now();
return [{
json: {
// Day info
weekday: now.weekday, // 1 = Monday, 7 = Sunday
weekdayShort: now.weekdayShort, // "Mon"
weekdayLong: now.weekdayLong, // "Monday"
isWeekend: now.weekday > 5, // Saturday or Sunday
// Month info
month: now.month, // 1-12
monthShort: now.monthShort, // "Jan"
monthLong: now.monthLong, // "January"
// Year info
year: now.year, // 2025
quarter: now.quarter, // 1-4
daysInMonth: now.daysInMonth // 28-31
}
}];---
3. $jmespath() - JSON Querying
Query and transform JSON structures using JMESPath syntax.
Basic Queries
const data = $input.first().json;
// Extract specific field
const names = $jmespath(data, 'users[*].name');
// Filter array
const adults = $jmespath(data, 'users[?age >= `18`]');
// Get specific index
const firstUser = $jmespath(data, 'users[0]');
return [{json: {names, adults, firstUser}}];Advanced Queries
const data = $input.first().json;
// Sort and slice
const top5 = $jmespath(data, 'users | sort_by(@, &score) | reverse(@) | [0:5]');
// Extract nested fields
const emails = $jmespath(data, 'users[*].contact.email');
// Multi-field extraction
const simplified = $jmespath(data, 'users[*].{name: name, email: contact.email}');
// Conditional filtering
const premium = $jmespath(data, 'users[?subscription.tier == `premium`]');
return [{json: {top5, emails, simplified, premium}}];Common Patterns
// Pattern 1: Filter and project
const query1 = $jmespath(data, 'products[?price > `100`].{name: name, price: price}');
// Pattern 2: Aggregate functions
const query2 = $jmespath(data, 'sum(products[*].price)');
const query3 = $jmespath(data, 'max(products[*].price)');
const query4 = $jmespath(data, 'length(products)');
// Pattern 3: Nested filtering
const query5 = $jmespath(data, 'categories[*].products[?inStock == `true`]');
return [{json: {query1, query2, query3, query4, query5}}];---
4. $getWorkflowStaticData() - Persistent Storage
Store data that persists across workflow executions.
Basic Usage
// Get static data storage
const staticData = $getWorkflowStaticData();
// Initialize counter if doesn't exist
if (!staticData.counter) {
staticData.counter = 0;
}
// Increment counter
staticData.counter++;
return [{
json: {
executionCount: staticData.counter
}
}];Use Cases
// Use Case 1: Rate limiting
const staticData = $getWorkflowStaticData();
const now = Date.now();
if (!staticData.lastRun) {
staticData.lastRun = now;
staticData.runCount = 1;
} else {
const timeSinceLastRun = now - staticData.lastRun;
if (timeSinceLastRun < 60000) { // Less than 1 minute
return [{json: {error: 'Rate limit: wait 1 minute between runs'}}];
}
staticData.lastRun = now;
staticData.runCount++;
}
return [{json: {allowed: true, totalRuns: staticData.runCount}}];// Use Case 2: Tracking last processed ID
const staticData = $getWorkflowStaticData();
const currentItems = $input.all();
// Get last processed ID
const lastId = staticData.lastProcessedId || 0;
// Filter only new items
const newItems = currentItems.filter(item => item.json.id > lastId);
// Update last processed ID
if (newItems.length > 0) {
staticData.lastProcessedId = Math.max(...newItems.map(item => item.json.id));
}
return newItems;// Use Case 3: Accumulating results
const staticData = $getWorkflowStaticData();
if (!staticData.accumulated) {
staticData.accumulated = [];
}
// Add current items to accumulated list
const currentData = $input.all().map(item => item.json);
staticData.accumulated.push(...currentData);
return [{
json: {
currentBatch: currentData.length,
totalAccumulated: staticData.accumulated.length,
allData: staticData.accumulated
}
}];---
5. Standard JavaScript Globals
Math Object
return [{
json: {
// Rounding
rounded: Math.round(3.7), // 4
floor: Math.floor(3.7), // 3
ceil: Math.ceil(3.2), // 4
// Min/Max
max: Math.max(1, 5, 3, 9, 2), // 9
min: Math.min(1, 5, 3, 9, 2), // 1
// Random
random: Math.random(), // 0-1
randomInt: Math.floor(Math.random() * 100), // 0-99
// Other
abs: Math.abs(-5), // 5
sqrt: Math.sqrt(16), // 4
pow: Math.pow(2, 3) // 8
}
}];JSON Object
// Parse JSON string
const jsonString = '{"name": "John", "age": 30}';
const parsed = JSON.parse(jsonString);
// Stringify object
const obj = {name: "John", age: 30};
const stringified = JSON.stringify(obj);
// Pretty print
const pretty = JSON.stringify(obj, null, 2);
return [{json: {parsed, stringified, pretty}}];console Object
// Debug logging (appears in browser console, press F12)
console.log('Processing items:', $input.all().length);
console.log('First item:', $input.first().json);
// Other console methods
console.error('Error message');
console.warn('Warning message');
console.info('Info message');
// Continues to return data
return [{json: {processed: true}}];Object Methods
const obj = {name: "John", age: 30, city: "NYC"};
return [{
json: {
keys: Object.keys(obj), // ["name", "age", "city"]
values: Object.values(obj), // ["John", 30, "NYC"]
entries: Object.entries(obj), // [["name", "John"], ...]
// Check property
hasName: 'name' in obj, // true
// Merge objects
merged: Object.assign({}, obj, {country: "USA"})
}
}];Array Methods
const arr = [1, 2, 3, 4, 5];
return [{
json: {
mapped: arr.map(x => x * 2), // [2, 4, 6, 8, 10]
filtered: arr.filter(x => x > 2), // [3, 4, 5]
reduced: arr.reduce((sum, x) => sum + x, 0), // 15
some: arr.some(x => x > 3), // true
every: arr.every(x => x > 0), // true
find: arr.find(x => x > 3), // 4
includes: arr.includes(3), // true
joined: arr.join(', ') // "1, 2, 3, 4, 5"
}
}];---
6. Available Node.js Modules
crypto Module
const crypto = require('crypto');
// Hash functions
const hash = crypto.createHash('sha256')
.update('my secret text')
.digest('hex');
// MD5 hash
const md5 = crypto.createHash('md5')
.update('my text')
.digest('hex');
// Random values
const randomBytes = crypto.randomBytes(16).toString('hex');
return [{json: {hash, md5, randomBytes}}];Buffer (built-in)
// Base64 encoding
const encoded = Buffer.from('Hello World').toString('base64');
// Base64 decoding
const decoded = Buffer.from(encoded, 'base64').toString();
// Hex encoding
const hex = Buffer.from('Hello').toString('hex');
return [{json: {encoded, decoded, hex}}];URL / URLSearchParams
// Parse URL
const url = new URL('https://example.com/path?param1=value1¶m2=value2');
// Build query string
const params = new URLSearchParams({
search: 'query',
page: 1,
limit: 10
});
return [{
json: {
host: url.host,
pathname: url.pathname,
search: url.search,
queryString: params.toString() // "search=query&page=1&limit=10"
}
}];---
What's NOT Available
External npm packages are NOT available:
- ❌ axios
- ❌ lodash
- ❌ moment (use DateTime/Luxon instead)
- ❌ request
- ❌ Any other npm package
Workaround: Use $helpers.httpRequest() for HTTP, or add data to workflow via HTTP Request node.
---
Summary
Most Useful Built-ins: 1. $helpers.httpRequest() - API calls without HTTP Request node 2. DateTime - Professional date/time handling 3. $jmespath() - Complex JSON queries 4. Math, JSON, Object, Array - Standard JavaScript utilities
Common Patterns:
- API calls: Use $helpers.httpRequest()
- Date operations: Use DateTime (Luxon)
- Data filtering: Use $jmespath() or JavaScript .filter()
- Persistent data: Use $getWorkflowStaticData()
- Hashing: Use crypto module
See Also:
- SKILL.md - Overview
- COMMON_PATTERNS.md - Real usage examples
- ERROR_PATTERNS.md - Error prevention
Common Patterns - JavaScript Code Node
Production-tested patterns for n8n Code nodes. These patterns are proven in real workflows.
---
Overview
This guide covers the 10 most useful Code node patterns for n8n workflows. Each pattern includes:
- Use Case: When to use this pattern
- Key Techniques: Important coding techniques demonstrated
- Complete Example: Working code you can adapt
- Variations: Common modifications
Pattern Categories:
- Data Aggregation (Patterns 1, 5, 10)
- Content Processing (Patterns 2, 3)
- Data Validation & Comparison (Patterns 4)
- Data Transformation (Patterns 5, 6, 7)
- Output Formatting (Pattern 8)
- Filtering & Ranking (Pattern 9)
---
Pattern 1: Multi-Source Data Aggregation
Use Case: Combining data from multiple APIs, RSS feeds, webhooks, or databases
When to use:
- Collecting data from multiple services
- Normalizing different API response formats
- Merging data sources into unified structure
- Building aggregated reports
Key Techniques: Loop iteration, conditional parsing, data normalization
Complete Example
// Process and structure data collected 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 - Hacker News format
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,
fetchedAt: new Date().toISOString()
});
}
}
// Parse source-specific structure - Reddit format
else if (sourceName === 'Reddit' && sourceData.data?.children) {
for (const post of sourceData.data.children) {
processedArticles.push({
title: post.data.title,
url: post.data.url,
summary: post.data.selftext || 'No summary',
source: 'Reddit',
score: post.data.score || 0,
fetchedAt: new Date().toISOString()
});
}
}
// Parse source-specific structure - RSS feed format
else if (sourceName === 'RSS' && sourceData.items) {
for (const rssItem of sourceData.items) {
processedArticles.push({
title: rssItem.title,
url: rssItem.link,
summary: rssItem.description || 'No summary',
source: 'RSS Feed',
score: 0,
fetchedAt: new Date().toISOString()
});
}
}
}
// Sort by score (highest first)
processedArticles.sort((a, b) => b.score - a.score);
return processedArticles.map(article => ({json: article}));Variations
// Variation 1: Add source weighting
for (const article of processedArticles) {
const weights = {
'Hacker News': 1.5,
'Reddit': 1.0,
'RSS Feed': 0.8
};
article.weightedScore = article.score * (weights[article.source] || 1.0);
}
// Variation 2: Filter by minimum score
processedArticles = processedArticles.filter(article => article.score >= 10);
// Variation 3: Deduplicate by URL
const seen = new Set();
processedArticles = processedArticles.filter(article => {
if (seen.has(article.url)) {
return false;
}
seen.add(article.url);
return true;
});---
Pattern 2: Regex Filtering & Pattern Matching
Use Case: Content analysis, keyword extraction, mention tracking, text parsing
When to use:
- Extracting mentions or tags from text
- Finding patterns in unstructured data
- Counting keyword occurrences
- Validating formats (emails, phone numbers)
Key Techniques: Regex matching, object aggregation, sorting/ranking
Complete Example
// Extract and track mentions using regex patterns
const etfPattern = /\b([A-Z]{2,5})\b/g;
const knownETFs = ['VOO', 'VTI', 'VT', 'SCHD', 'QYLD', 'VXUS', 'SPY', 'QQQ'];
const etfMentions = {};
for (const item of $input.all()) {
const data = item.json.data;
// Skip if no data or children
if (!data?.children) continue;
for (const post of data.children) {
// Combine title and body text
const title = post.data.title || '';
const body = post.data.selftext || '';
const combinedText = (title + ' ' + body).toUpperCase();
// Find all matches
const matches = combinedText.match(etfPattern);
if (matches) {
for (const match of matches) {
// Only count known ETFs
if (knownETFs.includes(match)) {
if (!etfMentions[match]) {
etfMentions[match] = {
count: 0,
totalScore: 0,
posts: []
};
}
etfMentions[match].count++;
etfMentions[match].totalScore += post.data.score || 0;
etfMentions[match].posts.push({
title: post.data.title,
url: post.data.url,
score: post.data.score
});
}
}
}
}
}
// Convert to array and sort by mention count
return Object.entries(etfMentions)
.map(([etf, data]) => ({
json: {
etf,
mentions: data.count,
totalScore: data.totalScore,
averageScore: data.totalScore / data.count,
topPosts: data.posts
.sort((a, b) => b.score - a.score)
.slice(0, 3)
}
}))
.sort((a, b) => b.json.mentions - a.json.mentions);Variations
// Variation 1: Email extraction
const emailPattern = /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/g;
const emails = text.match(emailPattern) || [];
// Variation 2: Phone number extraction
const phonePattern = /\b\d{3}[-.]?\d{3}[-.]?\d{4}\b/g;
const phones = text.match(phonePattern) || [];
// Variation 3: Hashtag extraction
const hashtagPattern = /#(\w+)/g;
const hashtags = [];
let match;
while ((match = hashtagPattern.exec(text)) !== null) {
hashtags.push(match[1]);
}
// Variation 4: URL extraction
const urlPattern = /https?:\/\/[^\s]+/g;
const urls = text.match(urlPattern) || [];---
Pattern 3: Markdown Parsing & Structured Data Extraction
Use Case: Parsing formatted text, extracting structured fields, content transformation
When to use:
- Parsing markdown or HTML
- Extracting data from structured text
- Converting formatted content to JSON
- Processing documentation or articles
Key Techniques: Regex grouping, helper functions, data normalization, while loops for iteration
Complete Example
// Parse markdown and extract structured information
const markdown = $input.first().json.data.markdown;
const adRegex = /##\s*(.*?)\n(.*?)(?=\n##|\n---|$)/gs;
const ads = [];
let match;
// Helper function to parse time strings to minutes
function parseTimeToMinutes(timeStr) {
if (!timeStr) return 999999; // Sort unparseable times last
const hourMatch = timeStr.match(/(\d+)\s*hour/);
const dayMatch = timeStr.match(/(\d+)\s*day/);
const minMatch = timeStr.match(/(\d+)\s*min/);
let totalMinutes = 0;
if (dayMatch) totalMinutes += parseInt(dayMatch[1]) * 1440; // 24 * 60
if (hourMatch) totalMinutes += parseInt(hourMatch[1]) * 60;
if (minMatch) totalMinutes += parseInt(minMatch[1]);
return totalMinutes;
}
// Extract all job postings from markdown
while ((match = adRegex.exec(markdown)) !== null) {
const title = match[1]?.trim() || 'No title';
const content = match[2]?.trim() || '';
// Extract structured fields from content
const districtMatch = content.match(/\*\*District:\*\*\s*(.*?)(?:\n|$)/);
const salaryMatch = content.match(/\*\*Salary:\*\*\s*(.*?)(?:\n|$)/);
const timeMatch = content.match(/Posted:\s*(.*?)\*/);
ads.push({
title: title,
district: districtMatch?.[1].trim() || 'Unknown',
salary: salaryMatch?.[1].trim() || 'Not specified',
postedTimeAgo: timeMatch?.[1] || 'Unknown',
timeInMinutes: parseTimeToMinutes(timeMatch?.[1]),
fullContent: content,
extractedAt: new Date().toISOString()
});
}
// Sort by recency (posted time)
ads.sort((a, b) => a.timeInMinutes - b.timeInMinutes);
return ads.map(ad => ({json: ad}));Variations
// Variation 1: Parse HTML table to JSON
const tableRegex = /<tr>(.*?)<\/tr>/gs;
const cellRegex = /<td>(.*?)<\/td>/g;
const rows = [];
let tableMatch;
while ((tableMatch = tableRegex.exec(htmlTable)) !== null) {
const cells = [];
let cellMatch;
while ((cellMatch = cellRegex.exec(tableMatch[1])) !== null) {
cells.push(cellMatch[1].trim());
}
if (cells.length > 0) {
rows.push(cells);
}
}
// Variation 2: Extract code blocks from markdown
const codeBlockRegex = /```(\w+)?\n(.*?)```/gs;
const codeBlocks = [];
while ((match = codeBlockRegex.exec(markdown)) !== null) {
codeBlocks.push({
language: match[1] || 'plain',
code: match[2].trim()
});
}
// Variation 3: Parse YAML frontmatter
const frontmatterRegex = /^---\n(.*?)\n---/s;
const frontmatterMatch = content.match(frontmatterRegex);
if (frontmatterMatch) {
const yamlLines = frontmatterMatch[1].split('\n');
const metadata = {};
for (const line of yamlLines) {
const [key, ...valueParts] = line.split(':');
if (key && valueParts.length > 0) {
metadata[key.trim()] = valueParts.join(':').trim();
}
}
}---
Pattern 4: JSON Comparison & Validation
Use Case: Workflow versioning, configuration validation, change detection, data integrity
When to use:
- Comparing two versions of data
- Detecting changes in configurations
- Validating data consistency
- Checking for differences
Key Techniques: JSON ordering, base64 decoding, deep comparison, object manipulation
Complete Example
// 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 allItems = $input.all();
// Assume first item is base64-encoded original, second is current
const origWorkflow = JSON.parse(
Buffer.from(allItems[0].json.content, 'base64').toString()
);
const currentWorkflow = allItems[1].json;
// Order keys for consistent comparison
const orderedOriginal = orderJsonKeys(origWorkflow);
const orderedCurrent = orderJsonKeys(currentWorkflow);
// Deep comparison
const isSame = JSON.stringify(orderedOriginal) === JSON.stringify(orderedCurrent);
// Find differences
const differences = [];
for (const key of Object.keys(orderedOriginal)) {
if (JSON.stringify(orderedOriginal[key]) !== JSON.stringify(orderedCurrent[key])) {
differences.push({
field: key,
original: orderedOriginal[key],
current: orderedCurrent[key]
});
}
}
// Check for new keys
for (const key of Object.keys(orderedCurrent)) {
if (!(key in orderedOriginal)) {
differences.push({
field: key,
original: null,
current: orderedCurrent[key],
status: 'new'
});
}
}
return [{
json: {
identical: isSame,
differenceCount: differences.length,
differences: differences,
original: orderedOriginal,
current: orderedCurrent,
comparedAt: new Date().toISOString()
}
}];Variations
// Variation 1: Simple equality check
const isEqual = JSON.stringify(obj1) === JSON.stringify(obj2);
// Variation 2: Deep diff with detailed changes
function deepDiff(obj1, obj2, path = '') {
const changes = [];
for (const key in obj1) {
const currentPath = path ? `${path}.${key}` : key;
if (!(key in obj2)) {
changes.push({type: 'removed', path: currentPath, value: obj1[key]});
} else if (typeof obj1[key] === 'object' && typeof obj2[key] === 'object') {
changes.push(...deepDiff(obj1[key], obj2[key], currentPath));
} else if (obj1[key] !== obj2[key]) {
changes.push({
type: 'modified',
path: currentPath,
from: obj1[key],
to: obj2[key]
});
}
}
for (const key in obj2) {
if (!(key in obj1)) {
const currentPath = path ? `${path}.${key}` : key;
changes.push({type: 'added', path: currentPath, value: obj2[key]});
}
}
return changes;
}
// Variation 3: Schema validation
function validateSchema(data, schema) {
const errors = [];
for (const field of schema.required || []) {
if (!(field in data)) {
errors.push(`Missing required field: ${field}`);
}
}
for (const [field, type] of Object.entries(schema.types || {})) {
if (field in data && typeof data[field] !== type) {
errors.push(`Field ${field} should be ${type}, got ${typeof data[field]}`);
}
}
return {
valid: errors.length === 0,
errors
};
}---
Pattern 5: CRM Data Transformation
Use Case: Lead enrichment, data normalization, API preparation, form data processing
When to use:
- Processing form submissions
- Preparing data for CRM APIs
- Normalizing contact information
- Enriching lead data
Key Techniques: Object destructuring, data mapping, format conversion, field splitting
Complete Example
// Transform form data into CRM-compatible format
const item = $input.all()[0];
const {
name,
email,
phone,
company,
course_interest,
message,
timestamp
} = item.json;
// Split name into first and last
const nameParts = name.split(' ');
const firstName = nameParts[0] || '';
const lastName = nameParts.slice(1).join(' ') || 'Unknown';
// Format phone number
const cleanPhone = phone.replace(/[^\d]/g, ''); // Remove non-digits
// Build CRM data structure
const crmData = {
data: {
type: 'Contact',
attributes: {
first_name: firstName,
last_name: lastName,
email1: email,
phone_work: cleanPhone,
account_name: company,
description: `Course Interest: ${course_interest}\n\nMessage: ${message}\n\nSubmitted: ${timestamp}`,
lead_source: 'Website Form',
status: 'New'
}
},
metadata: {
original_submission: timestamp,
processed_at: new Date().toISOString()
}
};
return [{
json: {
...item.json,
crmData,
processed: true
}
}];Variations
// Variation 1: Multiple contact processing
const contacts = $input.all();
return contacts.map(item => {
const data = item.json;
const [firstName, ...lastNameParts] = data.name.split(' ');
return {
json: {
firstName,
lastName: lastNameParts.join(' ') || 'Unknown',
email: data.email.toLowerCase(),
phone: data.phone.replace(/[^\d]/g, ''),
tags: [data.source, data.interest_level].filter(Boolean)
}
};
});
// Variation 2: Field validation and normalization
function normalizePContact(raw) {
return {
first_name: raw.firstName?.trim() || '',
last_name: raw.lastName?.trim() || 'Unknown',
email: raw.email?.toLowerCase().trim() || '',
phone: raw.phone?.replace(/[^\d]/g, '') || '',
company: raw.company?.trim() || 'Unknown',
title: raw.title?.trim() || '',
valid: Boolean(raw.email && raw.firstName)
};
}
// Variation 3: Lead scoring
function calculateLeadScore(data) {
let score = 0;
if (data.email) score += 10;
if (data.phone) score += 10;
if (data.company) score += 15;
if (data.title?.toLowerCase().includes('director')) score += 20;
if (data.title?.toLowerCase().includes('manager')) score += 15;
if (data.message?.length > 100) score += 10;
return score;
}---
Pattern 6: Release Information Processing
Use Case: Version management, changelog parsing, release notes generation, GitHub API processing
When to use:
- Processing GitHub releases
- Filtering stable versions
- Generating changelog summaries
- Extracting version information
Key Techniques: Array filtering, conditional field extraction, date formatting, string manipulation
Complete Example
// 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 => {
// Extract highlights section from changelog
const body = release.body || '';
let highlights = 'No highlights available';
if (body.includes('## Highlights:')) {
highlights = body.split('## Highlights:')[1]?.split('##')[0]?.trim();
} else {
// Fallback to first 500 chars
highlights = body.substring(0, 500) + '...';
}
return {
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: body,
highlights: highlights,
assetCount: release.assets.length,
assets: release.assets.map(asset => ({
name: asset.name,
size: asset.size,
downloadCount: asset.download_count,
downloadUrl: asset.browser_download_url
}))
};
});
return stableReleases.map(release => ({json: release}));Variations
// Variation 1: Version comparison
function compareVersions(v1, v2) {
const parts1 = v1.replace('v', '').split('.').map(Number);
const parts2 = v2.replace('v', '').split('.').map(Number);
for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
const num1 = parts1[i] || 0;
const num2 = parts2[i] || 0;
if (num1 > num2) return 1;
if (num1 < num2) return -1;
}
return 0;
}
// Variation 2: Breaking change detection
function hasBreakingChanges(changelog) {
const breakingKeywords = [
'BREAKING CHANGE',
'breaking change',
'BC:',
'💥'
];
return breakingKeywords.some(keyword => changelog.includes(keyword));
}
// Variation 3: Extract version numbers
const versionPattern = /v?(\d+)\.(\d+)\.(\d+)/;
const match = tagName.match(versionPattern);
if (match) {
const [_, major, minor, patch] = match;
const version = {major: parseInt(major), minor: parseInt(minor), patch: parseInt(patch)};
}---
Pattern 7: Array Transformation with Context
Use Case: Quick data transformation, field mapping, adding computed fields
When to use:
- Transforming arrays with additional context
- Adding calculated fields
- Simplifying complex objects
- Pluralization logic
Key Techniques: Array methods chaining, ternary operators, computed properties
Complete Example
// Transform releases with contextual information
const releases = $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',
downloadUrl: release.html_url,
isRecent: new Date(release.published_at) > new Date(Date.now() - 30 * 24 * 60 * 60 * 1000),
age: Math.floor((Date.now() - new Date(release.published_at)) / (24 * 60 * 60 * 1000)),
ageText: `${Math.floor((Date.now() - new Date(release.published_at)) / (24 * 60 * 60 * 1000))} days ago`
}));
return releases.map(release => ({json: release}));Variations
// Variation 1: Add ranking
const items = $input.all()
.sort((a, b) => b.json.score - a.json.score)
.map((item, index) => ({
json: {
...item.json,
rank: index + 1,
medal: index < 3 ? ['🥇', '🥈', '🥉'][index] : ''
}
}));
// Variation 2: Add percentage calculations
const total = $input.all().reduce((sum, item) => sum + item.json.value, 0);
const itemsWithPercentage = $input.all().map(item => ({
json: {
...item.json,
percentage: ((item.json.value / total) * 100).toFixed(2) + '%'
}
}));
// Variation 3: Add category labels
const categorize = (value) => {
if (value > 100) return 'High';
if (value > 50) return 'Medium';
return 'Low';
};
const categorized = $input.all().map(item => ({
json: {
...item.json,
category: categorize(item.json.value)
}
}));---
Pattern 8: Slack Block Kit Formatting
Use Case: Chat notifications, rich message formatting, interactive messages
When to use:
- Sending formatted Slack messages
- Creating interactive notifications
- Building rich content for chat platforms
- Status reports and alerts
Key Techniques: Template literals, nested objects, Block Kit syntax, date formatting
Complete Example
// Create Slack-formatted message with structured blocks
const date = new Date().toISOString().split('T')[0];
const data = $input.first().json;
return [{
json: {
text: `Daily Report - ${date}`, // Fallback text
blocks: [
{
type: "header",
text: {
type: "plain_text",
text: `📊 Daily Security Report - ${date}`
}
},
{
type: "section",
text: {
type: "mrkdwn",
text: `*Status:* ${data.status === 'ok' ? '✅ All Clear' : '⚠️ Issues Detected'}\n*Alerts:* ${data.alertCount || 0}\n*Updated:* ${new Date().toLocaleString()}`
}
},
{
type: "divider"
},
{
type: "section",
fields: [
{
type: "mrkdwn",
text: `*Failed Logins:*\n${data.failedLogins || 0}`
},
{
type: "mrkdwn",
text: `*API Errors:*\n${data.apiErrors || 0}`
},
{
type: "mrkdwn",
text: `*Uptime:*\n${data.uptime || '100%'}`
},
{
type: "mrkdwn",
text: `*Response Time:*\n${data.avgResponseTime || 'N/A'}ms`
}
]
},
{
type: "context",
elements: [{
type: "mrkdwn",
text: `Report generated automatically by n8n workflow`
}]
}
]
}
}];Variations
// Variation 1: Interactive buttons
const blocksWithButtons = [
{
type: "section",
text: {
type: "mrkdwn",
text: "Would you like to approve this request?"
},
accessory: {
type: "button",
text: {
type: "plain_text",
text: "Approve"
},
style: "primary",
value: "approve",
action_id: "approve_button"
}
}
];
// Variation 2: List formatting
const items = ['Item 1', 'Item 2', 'Item 3'];
const formattedList = items.map((item, i) => `${i + 1}. ${item}`).join('\n');
// Variation 3: Status indicators
function getStatusEmoji(status) {
const statusMap = {
'success': '✅',
'warning': '⚠️',
'error': '❌',
'info': 'ℹ️'
};
return statusMap[status] || '•';
}
// Variation 4: Truncate long messages
function truncate(text, maxLength = 3000) {
if (text.length <= maxLength) return text;
return text.substring(0, maxLength - 3) + '...';
}---
Pattern 9: Top N Filtering & Ranking
Use Case: RAG pipelines, ranking algorithms, result filtering, leaderboards
When to use:
- Getting top results by score
- Filtering best/worst performers
- Building leaderboards
- Relevance ranking
Key Techniques: Sorting, slicing, null coalescing, score calculations
Complete Example
// Filter and rank by similarity score, return top results
const ragResponse = $input.item.json;
const chunks = ragResponse.chunks || [];
// Sort by similarity (highest first)
const topChunks = chunks
.sort((a, b) => (b.similarity || 0) - (a.similarity || 0))
.slice(0, 6);
return [{
json: {
query: ragResponse.query,
topChunks: topChunks,
count: topChunks.length,
maxSimilarity: topChunks[0]?.similarity || 0,
minSimilarity: topChunks[topChunks.length - 1]?.similarity || 0,
averageSimilarity: topChunks.reduce((sum, chunk) => sum + (chunk.similarity || 0), 0) / topChunks.length
}
}];Variations
// Variation 1: Top N with minimum threshold
const threshold = 0.7;
const topItems = $input.all()
.filter(item => item.json.score >= threshold)
.sort((a, b) => b.json.score - a.json.score)
.slice(0, 10);
// Variation 2: Bottom N (worst performers)
const bottomItems = $input.all()
.sort((a, b) => a.json.score - b.json.score) // Ascending
.slice(0, 5);
// Variation 3: Top N by multiple criteria
const ranked = $input.all()
.map(item => ({
...item,
compositeScore: (item.json.relevance * 0.6) + (item.json.recency * 0.4)
}))
.sort((a, b) => b.compositeScore - a.compositeScore)
.slice(0, 10);
// Variation 4: Percentile filtering
const allScores = $input.all().map(item => item.json.score).sort((a, b) => b - a);
const percentile95 = allScores[Math.floor(allScores.length * 0.05)];
const topPercentile = $input.all().filter(item => item.json.score >= percentile95);---
Pattern 10: String Aggregation & Reporting
Use Case: Report generation, log aggregation, content concatenation, summary creation
When to use:
- Combining multiple text outputs
- Generating reports from data
- Aggregating logs or messages
- Creating formatted summaries
Key Techniques: Array joining, string concatenation, template literals, timestamp handling
Complete Example
// Aggregate multiple text inputs into formatted report
const allItems = $input.all();
// Collect all messages
const messages = allItems.map(item => item.json.message);
// Build report
const header = `🎯 **Daily Summary Report**\n📅 ${new Date().toLocaleString()}\n📊 Total Items: ${messages.length}\n\n`;
const divider = '\n\n---\n\n';
const footer = `\n\n---\n\n✅ Report generated at ${new Date().toISOString()}`;
const finalReport = header + messages.join(divider) + footer;
return [{
json: {
report: finalReport,
messageCount: messages.length,
generatedAt: new Date().toISOString(),
reportLength: finalReport.length
}
}];Variations
// Variation 1: Numbered list
const numberedReport = allItems
.map((item, index) => `${index + 1}. ${item.json.title}\n ${item.json.description}`)
.join('\n\n');
// Variation 2: Markdown table
const headers = '| Name | Status | Score |\n|------|--------|-------|\n';
const rows = allItems
.map(item => `| ${item.json.name} | ${item.json.status} | ${item.json.score} |`)
.join('\n');
const table = headers + rows;
// Variation 3: HTML report
const htmlReport = `
<!DOCTYPE html>
<html>
<head><title>Report</title></head>
<body>
<h1>Report - ${new Date().toLocaleDateString()}</h1>
<ul>
${allItems.map(item => `<li>${item.json.title}: ${item.json.value}</li>`).join('\n ')}
</ul>
</body>
</html>
`;
// Variation 4: JSON summary
const summary = {
generated: new Date().toISOString(),
totalItems: allItems.length,
items: allItems.map(item => item.json),
statistics: {
total: allItems.reduce((sum, item) => sum + (item.json.value || 0), 0),
average: allItems.reduce((sum, item) => sum + (item.json.value || 0), 0) / allItems.length,
max: Math.max(...allItems.map(item => item.json.value || 0)),
min: Math.min(...allItems.map(item => item.json.value || 0))
}
};---
Choosing the Right Pattern
Pattern Selection Guide
| Your Goal | Use Pattern |
|---|---|
| Combine multiple API responses | Pattern 1 (Multi-source Aggregation) |
| Extract mentions or keywords | Pattern 2 (Regex Filtering) |
| Parse formatted text | Pattern 3 (Markdown Parsing) |
| Detect changes in data | Pattern 4 (JSON Comparison) |
| Prepare form data for CRM | Pattern 5 (CRM Transformation) |
| Process GitHub releases | Pattern 6 (Release Processing) |
| Add computed fields | Pattern 7 (Array Transformation) |
| Format Slack messages | Pattern 8 (Block Kit Formatting) |
| Get top results | Pattern 9 (Top N Filtering) |
| Create text reports | Pattern 10 (String Aggregation) |
Combining Patterns
Many real workflows combine multiple patterns:
// Example: Multi-source aggregation + Top N filtering
const allItems = $input.all();
const aggregated = [];
// Pattern 1: Aggregate from different sources
for (const item of allItems) {
// ... aggregation logic
aggregated.push(normalizedItem);
}
// Pattern 9: Get top 10 by score
const top10 = aggregated
.sort((a, b) => b.score - a.score)
.slice(0, 10);
// Pattern 10: Generate report
const report = `Top 10 Items:\n\n${top10.map((item, i) => `${i + 1}. ${item.title} (${item.score})`).join('\n')}`;
return [{json: {report, items: top10}}];---
Summary
Most Useful Patterns: 1. Multi-source Aggregation - Combining data from APIs, databases 2. Top N Filtering - Rankings, leaderboards, best results 3. Data Transformation - CRM data, field mapping, enrichment
Key Techniques Across Patterns:
- Array methods (map, filter, reduce, sort, slice)
- Regex for pattern matching
- Object manipulation and destructuring
- Error handling with optional chaining
- Template literals for formatting
See Also:
- DATA_ACCESS.md - Data access methods
- ERROR_PATTERNS.md - Avoid common mistakes
- BUILTIN_FUNCTIONS.md - Built-in helpers
Data Access Patterns - JavaScript Code Node
Comprehensive guide to accessing data in n8n Code nodes using JavaScript.
---
Overview
In n8n Code nodes, you access data from previous nodes using built-in variables and methods. Understanding which method to use is critical for correct workflow execution.
Data Access Priority (by common usage): 1. `$input.all()` - Most common - Batch operations, aggregations 2. `$input.first()` - Very common - Single item operations 3. `$input.item` - Common - Each Item mode only 4. `$node["NodeName"].json` - Specific node references 5. `$json` - Direct current item (legacy, use $input instead)
---
Pattern 1: $input.all() - Process All Items
Usage: Most common pattern for batch processing
When to use:
- Processing multiple records
- Aggregating data (sum, count, average)
- Filtering arrays
- Transforming datasets
- Comparing items
- Sorting or ranking
Basic Usage
// Get all items from previous node
const allItems = $input.all();
// allItems is an array of objects like:
// [
// {json: {id: 1, name: "Alice"}},
// {json: {id: 2, name: "Bob"}}
// ]
console.log(`Received ${allItems.length} items`);
return allItems;Example 1: Filter Active Items
const allItems = $input.all();
// Filter only active items
const activeItems = allItems.filter(item => item.json.status === 'active');
return activeItems;Example 2: Transform All Items
const allItems = $input.all();
// Map to new structure
const transformed = allItems.map(item => ({
json: {
id: item.json.id,
fullName: `${item.json.firstName} ${item.json.lastName}`,
email: item.json.email,
processedAt: new Date().toISOString()
}
}));
return transformed;Example 3: Aggregate Data
const allItems = $input.all();
// Calculate total
const total = allItems.reduce((sum, item) => {
return sum + (item.json.amount || 0);
}, 0);
return [{
json: {
total,
count: allItems.length,
average: total / allItems.length
}
}];Example 4: Sort and Limit
const allItems = $input.all();
// Get top 5 by score
const topFive = allItems
.sort((a, b) => (b.json.score || 0) - (a.json.score || 0))
.slice(0, 5);
return topFive.map(item => ({json: item.json}));Example 5: Group By Category
const allItems = $input.all();
// Group items by category
const grouped = {};
for (const item of allItems) {
const category = item.json.category || 'Uncategorized';
if (!grouped[category]) {
grouped[category] = [];
}
grouped[category].push(item.json);
}
// Convert to array format
return Object.entries(grouped).map(([category, items]) => ({
json: {
category,
items,
count: items.length
}
}));Example 6: Deduplicate by ID
const allItems = $input.all();
// Remove duplicates by ID
const seen = new Set();
const unique = [];
for (const item of allItems) {
const id = item.json.id;
if (!seen.has(id)) {
seen.add(id);
unique.push(item);
}
}
return unique;---
Pattern 2: $input.first() - Get First Item
Usage: Very common for single-item operations
When to use:
- Previous node returns single object
- Working with API responses
- Getting initial/first data point
- Configuration or metadata access
Basic Usage
// Get first item from previous node
const firstItem = $input.first();
// Access the JSON data
const data = firstItem.json;
console.log('First item:', data);
return [{json: data}];Example 1: Process Single API Response
// Get API response (typically single object)
const response = $input.first().json;
// Extract what you need
return [{
json: {
userId: response.data.user.id,
userName: response.data.user.name,
status: response.status,
fetchedAt: new Date().toISOString()
}
}];Example 2: Transform Single Object
const data = $input.first().json;
// Transform structure
return [{
json: {
id: data.id,
contact: {
email: data.email,
phone: data.phone
},
address: {
street: data.street,
city: data.city,
zip: data.zip
}
}
}];Example 3: Validate Single Item
const item = $input.first().json;
// Validation logic
const isValid = item.email && item.email.includes('@');
return [{
json: {
...item,
valid: isValid,
validatedAt: new Date().toISOString()
}
}];Example 4: Extract Nested Data
const response = $input.first().json;
// Navigate nested structure
const users = response.data?.users || [];
return users.map(user => ({
json: {
id: user.id,
name: user.profile?.name || 'Unknown',
email: user.contact?.email || 'no-email'
}
}));Example 5: Combine with Other Methods
// Get first item's data
const firstData = $input.first().json;
// Use it to filter all items
const allItems = $input.all();
const matching = allItems.filter(item =>
item.json.category === firstData.targetCategory
);
return matching;---
Pattern 3: $input.item - Current Item (Each Item Mode)
Usage: Common in "Run Once for Each Item" mode
When to use:
- Mode is set to "Run Once for Each Item"
- Need to process items independently
- Per-item API calls or validations
- Item-specific error handling
IMPORTANT: Only use in "Each Item" mode. Will be undefined in "All Items" mode.
Basic Usage
// In "Run Once for Each Item" mode
const currentItem = $input.item;
const data = currentItem.json;
console.log('Processing item:', data.id);
return [{
json: {
...data,
processed: true
}
}];Example 1: Add Processing Metadata
const item = $input.item;
return [{
json: {
...item.json,
processed: true,
processedAt: new Date().toISOString(),
processingDuration: Math.random() * 1000 // Simulated duration
}
}];Example 2: Per-Item Validation
const item = $input.item;
const data = item.json;
// Validate this specific item
const errors = [];
if (!data.email) errors.push('Email required');
if (!data.name) errors.push('Name required');
if (data.age && data.age < 18) errors.push('Must be 18+');
return [{
json: {
...data,
valid: errors.length === 0,
errors: errors.length > 0 ? errors : undefined
}
}];Example 3: Item-Specific API Call
const item = $input.item;
const userId = item.json.userId;
// Make API call specific to this item
const response = await $helpers.httpRequest({
method: 'GET',
url: `https://api.example.com/users/${userId}/details`
});
return [{
json: {
...item.json,
details: response
}
}];Example 4: Conditional Processing
const item = $input.item;
const data = item.json;
// Process based on item type
if (data.type === 'premium') {
return [{
json: {
...data,
discount: 0.20,
tier: 'premium'
}
}];
} else {
return [{
json: {
...data,
discount: 0.05,
tier: 'standard'
}
}];
}---
Pattern 4: $node - Reference Other Nodes
Usage: Less common, but powerful for specific scenarios
When to use:
- Need data from specific named node
- Combining data from multiple nodes
- Accessing metadata about workflow execution
Basic Usage
// Get output from specific node
const webhookData = $node["Webhook"].json;
const apiData = $node["HTTP Request"].json;
return [{
json: {
fromWebhook: webhookData,
fromAPI: apiData
}
}];Example 1: Combine Multiple Sources
// Reference multiple nodes
const webhook = $node["Webhook"].json;
const database = $node["Postgres"].json;
const api = $node["HTTP Request"].json;
return [{
json: {
combined: {
webhook: webhook.body,
dbRecords: database.length,
apiResponse: api.status
},
processedAt: new Date().toISOString()
}
}];Example 2: Compare Across Nodes
const oldData = $node["Get Old Data"].json;
const newData = $node["Get New Data"].json;
// Compare
const changes = {
added: newData.filter(n => !oldData.find(o => o.id === n.id)),
removed: oldData.filter(o => !newData.find(n => n.id === o.id)),
modified: newData.filter(n => {
const old = oldData.find(o => o.id === n.id);
return old && JSON.stringify(old) !== JSON.stringify(n);
})
};
return [{
json: {
changes,
summary: {
added: changes.added.length,
removed: changes.removed.length,
modified: changes.modified.length
}
}
}];Example 3: Access Node Metadata
// Get data from specific execution path
const ifTrueBranch = $node["IF True"].json;
const ifFalseBranch = $node["IF False"].json;
// Use whichever branch executed
const result = ifTrueBranch || ifFalseBranch || {};
return [{json: result}];---
Critical: Webhook Data Structure
MOST COMMON MISTAKE: Forgetting webhook data is nested under .body
The Problem
Webhook node wraps all incoming data under a body property. This catches many developers by surprise.
Structure
// Webhook node output structure:
{
"headers": {
"content-type": "application/json",
"user-agent": "...",
// ... other headers
},
"params": {},
"query": {},
"body": {
// ← YOUR DATA IS HERE
"name": "Alice",
"email": "alice@example.com",
"message": "Hello!"
}
}Wrong vs Right
// ❌ WRONG: Trying to access directly
const name = $json.name; // undefined
const email = $json.email; // undefined
// ✅ CORRECT: Access via .body
const name = $json.body.name; // "Alice"
const email = $json.body.email; // "alice@example.com"
// ✅ CORRECT: Extract body first
const webhookData = $json.body;
const name = webhookData.name; // "Alice"
const email = webhookData.email; // "alice@example.com"Example: Full Webhook Processing
// Get webhook data from previous node
const webhookOutput = $input.first().json;
// Access the actual payload
const payload = webhookOutput.body;
// Access headers if needed
const contentType = webhookOutput.headers['content-type'];
// Access query parameters if needed
const apiKey = webhookOutput.query.api_key;
// Process the actual data
return [{
json: {
// Data from webhook body
userName: payload.name,
userEmail: payload.email,
message: payload.message,
// Metadata
receivedAt: new Date().toISOString(),
contentType: contentType,
authenticated: !!apiKey
}
}];POST Data, Query Params, and Headers
const webhook = $input.first().json;
return [{
json: {
// POST body data
formData: webhook.body,
// Query parameters (?key=value)
queryParams: webhook.query,
// HTTP headers
userAgent: webhook.headers['user-agent'],
contentType: webhook.headers['content-type'],
// Request metadata
method: webhook.method, // POST, GET, etc.
url: webhook.url
}
}];Common Webhook Scenarios
// Scenario 1: Form submission
const formData = $json.body;
const name = formData.name;
const email = formData.email;
// Scenario 2: JSON API webhook
const apiPayload = $json.body;
const eventType = apiPayload.event;
const data = apiPayload.data;
// Scenario 3: Query parameters
const apiKey = $json.query.api_key;
const userId = $json.query.user_id;
// Scenario 4: Headers
const authorization = $json.headers['authorization'];
const signature = $json.headers['x-signature'];---
Choosing the Right Pattern
Decision Tree
Do you need ALL items from previous node?
├─ YES → Use $input.all()
│
└─ NO → Do you need just the FIRST item?
├─ YES → Use $input.first()
│
└─ NO → Are you in "Each Item" mode?
├─ YES → Use $input.item
│
└─ NO → Do you need specific node data?
├─ YES → Use $node["NodeName"]
└─ NO → Use $input.first() (default)Quick Reference Table
| Scenario | Use This | Example |
|---|---|---|
| Sum all amounts | $input.all() | allItems.reduce((sum, i) => sum + i.json.amount, 0) |
| Get API response | $input.first() | $input.first().json.data |
| Process each independently | $input.item | $input.item.json (Each Item mode) |
| Combine two nodes | $node["Name"] | $node["API"].json |
| Filter array | $input.all() | allItems.filter(i => i.json.active) |
| Transform single object | $input.first() | {...input.first().json, new: true} |
| Webhook data | $input.first() | $input.first().json.body |
---
Common Mistakes
Mistake 1: Using $json Without Context
// ❌ WRONG: $json is ambiguous
const value = $json.field;
// ✅ CORRECT: Be explicit
const value = $input.first().json.field;Mistake 2: Forgetting .json Property
// ❌ WRONG: Trying to access fields on item object
const items = $input.all();
const names = items.map(item => item.name); // undefined
// ✅ CORRECT: Access via .json
const names = items.map(item => item.json.name);Mistake 3: Using $input.item in All Items Mode
// ❌ WRONG: $input.item is undefined in "All Items" mode
const data = $input.item.json; // Error!
// ✅ CORRECT: Use appropriate method
const data = $input.first().json; // Or $input.all()Mistake 4: Not Handling Empty Arrays
// ❌ WRONG: Crashes if no items
const first = $input.all()[0].json;
// ✅ CORRECT: Check length first
const items = $input.all();
if (items.length === 0) {
return [];
}
const first = items[0].json;
// ✅ ALSO CORRECT: Use $input.first()
const first = $input.first().json; // Built-in safetyMistake 5: Modifying Original Data
// ❌ RISKY: Mutating original
const items = $input.all();
items[0].json.modified = true; // Modifies original
return items;
// ✅ SAFE: Create new objects
const items = $input.all();
return items.map(item => ({
json: {
...item.json,
modified: true
}
}));---
Advanced Patterns
Pattern: Pagination Handling
const currentPage = $input.all();
const pageNumber = $node["Set Page"].json.page || 1;
// Combine with previous pages
const allPreviousPages = $node["Accumulator"]?.json.accumulated || [];
return [{
json: {
accumulated: [...allPreviousPages, ...currentPage],
currentPage: pageNumber,
totalItems: allPreviousPages.length + currentPage.length
}
}];Pattern: Conditional Node Reference
// Access different nodes based on condition
const condition = $input.first().json.type;
let data;
if (condition === 'api') {
data = $node["API Response"].json;
} else if (condition === 'database') {
data = $node["Database"].json;
} else {
data = $node["Default"].json;
}
return [{json: data}];Pattern: Multi-Node Aggregation
// Collect data from multiple named nodes
const sources = ['Source1', 'Source2', 'Source3'];
const allData = [];
for (const source of sources) {
const nodeData = $node[source]?.json;
if (nodeData) {
allData.push({
source,
data: nodeData
});
}
}
return allData.map(item => ({json: item}));---
Summary
Most Common Patterns: 1. $input.all() - Process multiple items, batch operations 2. $input.first() - Single item, API responses 3. $input.item - Each Item mode processing
Critical Rule:
- Webhook data is under
.bodyproperty
Best Practice:
- Be explicit: Use
$input.first().json.fieldinstead of$json.field - Always check for null/undefined
- Use appropriate method for your mode (All Items vs Each Item)
See Also:
- SKILL.md - Overview and quick start
- COMMON_PATTERNS.md - Production patterns
- ERROR_PATTERNS.md - Avoid common mistakes
Error Patterns - JavaScript Code Node
Complete guide to avoiding the most common Code node errors.
---
Overview
This guide covers the top 5 error patterns encountered in n8n Code nodes. Understanding and avoiding these errors will save you significant debugging time.
Error Frequency: 1. Empty Code / Missing Return - 38% of failures 2. Expression Syntax Confusion - 8% of failures 3. Incorrect Return Wrapper - 5% of failures 4. Unmatched Expression Brackets - 6% of failures 5. Missing Null Checks - Common runtime error
---
Error #1: Empty Code or Missing Return Statement
Frequency: Most common error (38% of all validation failures)
What Happens:
- Workflow execution fails
- Next nodes receive no data
- Error: "Code cannot be empty" or "Code must return data"
The Problem
// ❌ ERROR: No code at all
// (Empty code field)// ❌ ERROR: Code executes but doesn't return anything
const items = $input.all();
// Process items
for (const item of items) {
console.log(item.json.name);
}
// Forgot to return!// ❌ ERROR: Early return path exists, but not all paths return
const items = $input.all();
if (items.length === 0) {
return []; // ✅ This path returns
}
// Process items
const processed = items.map(item => ({json: item.json}));
// ❌ Forgot to return processed!The Solution
// ✅ CORRECT: Always return data
const items = $input.all();
// Process items
const processed = items.map(item => ({
json: {
...item.json,
processed: true
}
}));
return processed; // ✅ Return statement present// ✅ CORRECT: Return empty array if no items
const items = $input.all();
if (items.length === 0) {
return []; // Valid: empty array when no data
}
// Process and return
return items.map(item => ({json: item.json}));// ✅ CORRECT: All code paths return
const items = $input.all();
if (items.length === 0) {
return [];
} else if (items.length === 1) {
return [{json: {single: true, data: items[0].json}}];
} else {
return items.map(item => ({json: item.json}));
}
// All paths coveredChecklist
- [ ] Code field is not empty
- [ ] Return statement exists
- [ ] ALL code paths return data (if/else branches)
- [ ] Return format is correct (
[{json: {...}}]) - [ ] Return happens even on errors (use try-catch)
---
Error #2: Expression Syntax Confusion
Frequency: 8% of validation failures
What Happens:
- Syntax error in code execution
- Error: "Unexpected token" or "Expression syntax is not valid in Code nodes"
- Template variables not evaluated
The Problem
n8n has TWO distinct syntaxes: 1. Expression syntax {{ }} - Used in OTHER nodes (Set, IF, HTTP Request) 2. JavaScript - Used in CODE nodes (no {{ }})
Many developers mistakenly use expression syntax inside Code nodes.
// ❌ WRONG: Using n8n expression syntax in Code node
const userName = "{{ $json.name }}";
const userEmail = "{{ $json.body.email }}";
return [{
json: {
name: userName,
email: userEmail
}
}];
// Result: Literal string "{{ $json.name }}", NOT the value!// ❌ WRONG: Trying to evaluate expressions
const value = "{{ $now.toFormat('yyyy-MM-dd') }}";The Solution
// ✅ CORRECT: Use JavaScript directly (no {{ }})
const userName = $json.name;
const userEmail = $json.body.email;
return [{
json: {
name: userName,
email: userEmail
}
}];// ✅ CORRECT: JavaScript template literals (use backticks)
const message = `Hello, ${$json.name}! Your email is ${$json.email}`;
return [{
json: {
greeting: message
}
}];// ✅ CORRECT: Direct variable access
const item = $input.first().json;
return [{
json: {
name: item.name,
email: item.email,
timestamp: new Date().toISOString() // JavaScript Date, not {{ }}
}
}];Comparison Table
| Context | Syntax | Example |
|---|---|---|
| Set node | {{ }} expressions | {{ $json.name }} |
| IF node | {{ }} expressions | {{ $json.age > 18 }} |
| HTTP Request URL | {{ }} expressions | {{ $json.userId }} |
| Code node | JavaScript | $json.name |
| Code node strings | Template literals | ` Hello ${$json.name} ` |
Quick Fix Guide
// WRONG → RIGHT conversions
// ❌ "{{ $json.field }}"
// ✅ $json.field
// ❌ "{{ $now }}"
// ✅ new Date().toISOString()
// ❌ "{{ $node['HTTP Request'].json.data }}"
// ✅ $node["HTTP Request"].json.data
// ❌ `{{ $json.firstName }} {{ $json.lastName }}`
// ✅ `${$json.firstName} ${$json.lastName}`---
Error #3: Incorrect Return Wrapper Format
Frequency: 5% of validation failures
What Happens:
- Error: "Return value must be an array of objects"
- Error: "Each item must have a json property"
- Next nodes receive malformed data
The Problem
Code nodes MUST return:
- Array of objects
- Each object MUST have a `json` property
// ❌ WRONG: Returning object instead of array
return {
json: {
result: 'success'
}
};
// Missing array wrapper []// ❌ WRONG: Returning array without json wrapper
return [
{id: 1, name: 'Alice'},
{id: 2, name: 'Bob'}
];
// Missing json property// ❌ WRONG: Returning plain value
return "processed";// ❌ WRONG: Returning items without mapping
return $input.all();
// Works if items already have json property, but not guaranteed// ❌ WRONG: Incomplete structure
return [{data: {result: 'success'}}];
// Should be {json: {...}}, not {data: {...}}The Solution
// ✅ CORRECT: Single result
return [{
json: {
result: 'success',
timestamp: new Date().toISOString()
}
}];// ✅ CORRECT: Multiple results
return [
{json: {id: 1, name: 'Alice'}},
{json: {id: 2, name: 'Bob'}},
{json: {id: 3, name: 'Carol'}}
];// ✅ CORRECT: Transforming array
const items = $input.all();
return items.map(item => ({
json: {
id: item.json.id,
name: item.json.name,
processed: true
}
}));// ✅ CORRECT: Empty result
return [];
// Valid when no data to return// ✅ CORRECT: Conditional returns
if (shouldProcess) {
return [{json: {result: 'processed'}}];
} else {
return [];
}Return Format Checklist
- [ ] Return value is an array
[...] - [ ] Each array element has `json` property
- [ ] Structure is
[{json: {...}}]or[{json: {...}}, {json: {...}}] - [ ] NOT
{json: {...}}(missing array wrapper) - [ ] NOT
[{...}](missing json property)
Common Scenarios
// Scenario 1: Single object from API
const response = $input.first().json;
// ✅ CORRECT
return [{json: response}];
// ❌ WRONG
return {json: response};
// Scenario 2: Array of objects
const users = $input.all();
// ✅ CORRECT
return users.map(user => ({json: user.json}));
// ❌ WRONG
return users; // Risky - depends on existing structure
// Scenario 3: Computed result
const total = $input.all().reduce((sum, item) => sum + item.json.amount, 0);
// ✅ CORRECT
return [{json: {total}}];
// ❌ WRONG
return {total};
// Scenario 4: No results
// ✅ CORRECT
return [];
// ❌ WRONG
return null;---
Error #4: Unmatched Expression Brackets
Frequency: 6% of validation failures
What Happens:
- Parsing error during save
- Error: "Unmatched expression brackets"
- Code appears correct but fails validation
The Problem
This error typically occurs when: 1. Strings contain unbalanced quotes 2. Multi-line strings with special characters 3. Template literals with nested brackets
// ❌ WRONG: Unescaped quote in string
const message = "It's a nice day";
// Single quote breaks string// ❌ WRONG: Unbalanced brackets in regex
const pattern = /\{(\w+)\}/; // JSON storage issue// ❌ WRONG: Multi-line string with quotes
const html = "
<div class="container">
<p>Hello</p>
</div>
";
// Quote balance issuesThe Solution
// ✅ CORRECT: Escape quotes
const message = "It\\'s a nice day";
// Or use different quotes
const message = "It's a nice day"; // Double quotes work// ✅ CORRECT: Escape regex properly
const pattern = /\\{(\\w+)\\}/;// ✅ CORRECT: Template literals for multi-line
const html = `
<div class="container">
<p>Hello</p>
</div>
`;
// Backticks handle multi-line and quotes// ✅ CORRECT: Escape backslashes
const path = "C:\\\\Users\\\\Documents\\\\file.txt";Escaping Guide
| Character | Escape As | Example |
|---|---|---|
| Single quote in single-quoted string | \\' | 'It\\'s working' |
| Double quote in double-quoted string | \\" | "She said \\"hello\\"" |
| Backslash | \\\\ | "C:\\\\path" |
| Newline | \\n | "Line 1\\nLine 2" |
| Tab | \\t | "Column1\\tColumn2" |
Best Practices
// ✅ BEST: Use template literals for complex strings
const message = `User ${name} said: "Hello!"`;
// ✅ BEST: Use template literals for HTML
const html = `
<div class="${className}">
<h1>${title}</h1>
<p>${content}</p>
</div>
`;
// ✅ BEST: Use template literals for JSON
const jsonString = `{
"name": "${name}",
"email": "${email}"
}`;---
Error #5: Missing Null Checks / Undefined Access
Frequency: Very common runtime error
What Happens:
- Workflow execution stops
- Error: "Cannot read property 'X' of undefined"
- Error: "Cannot read property 'X' of null"
- Crashes on missing data
The Problem
// ❌ WRONG: No null check - crashes if user doesn't exist
const email = item.json.user.email;// ❌ WRONG: Assumes array has items
const firstItem = $input.all()[0].json;// ❌ WRONG: Assumes nested property exists
const city = $json.address.city;// ❌ WRONG: No validation before array operations
const names = $json.users.map(user => user.name);The Solution
// ✅ CORRECT: Optional chaining
const email = item.json?.user?.email || 'no-email@example.com';// ✅ CORRECT: Check array length
const items = $input.all();
if (items.length === 0) {
return [];
}
const firstItem = items[0].json;// ✅ CORRECT: Guard clauses
const data = $input.first().json;
if (!data.address) {
return [{json: {error: 'No address provided'}}];
}
const city = data.address.city;// ✅ CORRECT: Default values
const users = $json.users || [];
const names = users.map(user => user.name || 'Unknown');// ✅ CORRECT: Try-catch for risky operations
try {
const email = item.json.user.email.toLowerCase();
return [{json: {email}}];
} catch (error) {
return [{
json: {
error: 'Invalid user data',
details: error.message
}
}];
}Safe Access Patterns
// Pattern 1: Optional chaining (modern, recommended)
const value = data?.nested?.property?.value;
// Pattern 2: Logical OR with default
const value = data.property || 'default';
// Pattern 3: Ternary check
const value = data.property ? data.property : 'default';
// Pattern 4: Guard clause
if (!data.property) {
return [];
}
const value = data.property;
// Pattern 5: Try-catch
try {
const value = data.nested.property.value;
} catch (error) {
const value = 'default';
}Webhook Data Safety
// Webhook data requires extra safety
// ❌ RISKY: Assumes all fields exist
const name = $json.body.user.name;
const email = $json.body.user.email;
// ✅ SAFE: Check each level
const body = $json.body || {};
const user = body.user || {};
const name = user.name || 'Unknown';
const email = user.email || 'no-email';
// ✅ BETTER: Optional chaining
const name = $json.body?.user?.name || 'Unknown';
const email = $json.body?.user?.email || 'no-email';Array Safety
// ❌ RISKY: No length check
const items = $input.all();
const firstId = items[0].json.id;
// ✅ SAFE: Check length
const items = $input.all();
if (items.length > 0) {
const firstId = items[0].json.id;
} else {
// Handle empty case
return [];
}
// ✅ BETTER: Use $input.first()
const firstItem = $input.first();
const firstId = firstItem.json.id; // Built-in safetyObject Property Safety
// ❌ RISKY: Direct access
const config = $json.settings.advanced.timeout;
// ✅ SAFE: Step by step with defaults
const settings = $json.settings || {};
const advanced = settings.advanced || {};
const timeout = advanced.timeout || 30000;
// ✅ BETTER: Optional chaining
const timeout = $json.settings?.advanced?.timeout ?? 30000;
// Note: ?? (nullish coalescing) vs || (logical OR)---
Error Prevention Checklist
Use this checklist before deploying Code nodes:
Code Structure
- [ ] Code field is not empty
- [ ] Return statement exists
- [ ] All code paths return data
Return Format
- [ ] Returns array:
[...] - [ ] Each item has
jsonproperty:{json: {...}} - [ ] Format is
[{json: {...}}]
Syntax
- [ ] No
{{ }}expression syntax (use JavaScript) - [ ] Template literals use backticks: `
${variable}` - [ ] All quotes and brackets balanced
- [ ] Strings properly escaped
Data Safety
- [ ] Null checks for optional properties
- [ ] Array length checks before access
- [ ] Webhook data accessed via
.body - [ ] Try-catch for risky operations
- [ ] Default values for missing data
Testing
- [ ] Test with empty input
- [ ] Test with missing fields
- [ ] Test with unexpected data types
- [ ] Check browser console for errors
---
Quick Error Reference
| Error Message | Likely Cause | Fix |
|---|---|---|
| "Code cannot be empty" | Empty code field | Add meaningful code |
| "Code must return data" | Missing return statement | Add return [...] |
| "Return value must be an array" | Returning object instead of array | Wrap in [...] |
| "Each item must have json property" | Missing json wrapper | Use {json: {...}} |
| "Unexpected token" | Expression syntax {{ }} in code | Remove {{ }}, use JavaScript |
| "Cannot read property X of undefined" | Missing null check | Use optional chaining ?. |
| "Cannot read property X of null" | Null value access | Add guard clause or default |
| "Unmatched expression brackets" | Quote/bracket imbalance | Check string escaping |
---
Debugging Tips
1. Use console.log()
const items = $input.all();
console.log('Items count:', items.length);
console.log('First item:', items[0]);
// Check browser console (F12) for output2. Return Intermediate Results
// Debug by returning current state
const items = $input.all();
const processed = items.map(item => ({json: item.json}));
// Return to see what you have
return processed;3. Try-Catch for Troubleshooting
try {
// Your code here
const result = riskyOperation();
return [{json: {result}}];
} catch (error) {
// See what failed
return [{
json: {
error: error.message,
stack: error.stack
}
}];
}4. Validate Input Structure
const items = $input.all();
// Check what you received
console.log('Input structure:', JSON.stringify(items[0], null, 2));
// Then process---
Summary
Top 5 Errors to Avoid: 1. Empty code / missing return (38%) - Always return data 2. Expression syntax `{{ }}` (8%) - Use JavaScript, not expressions 3. Wrong return format (5%) - Always [{json: {...}}] 4. Unmatched brackets (6%) - Escape strings properly 5. Missing null checks - Use optional chaining ?.
Quick Prevention:
- Return
[{json: {...}}]format - Use JavaScript, NOT
{{ }}expressions - Check for null/undefined before accessing
- Test with empty and invalid data
- Use browser console for debugging
See Also:
- SKILL.md - Overview and best practices
- DATA_ACCESS.md - Safe data access patterns
- COMMON_PATTERNS.md - Working examples
n8n Code JavaScript
Expert guidance for writing JavaScript code in n8n Code nodes.
---
Purpose
Teaches how to write effective JavaScript in n8n Code nodes, avoid common errors, and use built-in functions effectively.
---
Activates On
Trigger keywords:
- "javascript code node"
- "write javascript in n8n"
- "code node javascript"
- "$input syntax"
- "$json syntax"
- "$helpers.httpRequest"
- "DateTime luxon"
- "code node error"
- "webhook data code"
- "return format code node"
Common scenarios:
- Writing JavaScript code in Code nodes
- Troubleshooting Code node errors
- Making HTTP requests from code
- Working with dates and times
- Accessing webhook data
- Choosing between All Items and Each Item mode
---
What You'll Learn
Quick Start
- Mode selection (All Items vs Each Item)
- Data access patterns ($input.all(), $input.first(), $input.item)
- Correct return format:
[{json: {...}}] - Webhook data structure (.body nesting)
- Built-in functions overview
Data Access Mastery
- $input.all() - Batch operations (most common)
- $input.first() - Single item operations
- $input.item - Each Item mode processing
- $node - Reference other workflow nodes
- Critical gotcha: Webhook data under
.body
Common Patterns (Production-Tested)
1. Multi-source Data Aggregation 2. Regex Filtering & Pattern Matching 3. Markdown Parsing & Structured Extraction 4. JSON Comparison & Validation 5. CRM Data Transformation 6. Release Information Processing 7. Array Transformation with Context 8. Slack Block Kit Formatting 9. Top N Filtering & Ranking 10. String Aggregation & Reporting
Error Prevention
Top 5 errors to avoid: 1. Empty code / missing return (38% of failures) 2. Expression syntax confusion (using {{}} in code) 3. Incorrect return format (missing array wrapper or json property) 4. Unmatched brackets (string escaping issues) 5. Missing null checks (crashes on undefined)
Built-in Functions
- $helpers.httpRequest() - Make HTTP requests
- DateTime (Luxon) - Advanced date/time operations
- $jmespath() - Query JSON structures
- $getWorkflowStaticData() - Persistent storage
- Standard JavaScript globals (Math, JSON, console)
- Available Node.js modules (crypto, Buffer, URL)
---
File Structure
n8n-code-javascript/
├── SKILL.md (500 lines)
│ Overview, quick start, mode selection, best practices
│ - Mode selection guide (All Items vs Each Item)
│ - Data access patterns overview
│ - Return format requirements
│ - Critical webhook gotcha
│ - Error prevention overview
│ - Quick reference checklist
│
├── DATA_ACCESS.md (400 lines)
│ Complete data access patterns
│ - $input.all() - Most common (26% usage)
│ - $input.first() - Very common (25% usage)
│ - $input.item - Each Item mode (19% usage)
│ - $node - Reference other nodes
│ - Webhook data structure (.body nesting)
│ - Choosing the right pattern
│ - Common mistakes to avoid
│
├── COMMON_PATTERNS.md (600 lines)
│ 10 production-tested patterns
│ - Pattern 1: Multi-source Aggregation
│ - Pattern 2: Regex Filtering
│ - Pattern 3: Markdown Parsing
│ - Pattern 4: JSON Comparison
│ - Pattern 5: CRM Transformation
│ - Pattern 6: Release Processing
│ - Pattern 7: Array Transformation
│ - Pattern 8: Slack Block Kit
│ - Pattern 9: Top N Filtering
│ - Pattern 10: String Aggregation
│ - Pattern selection guide
│
├── ERROR_PATTERNS.md (450 lines)
│ Top 5 errors with solutions
│ - Error #1: Empty Code / Missing Return (38%)
│ - Error #2: Expression Syntax Confusion (8%)
│ - Error #3: Incorrect Return Wrapper (5%)
│ - Error #4: Unmatched Brackets (6%)
│ - Error #5: Missing Null Checks
│ - Error prevention checklist
│ - Quick error reference
│ - Debugging tips
│
├── BUILTIN_FUNCTIONS.md (450 lines)
│ Complete built-in function reference
│ - $helpers.httpRequest() API reference
│ - DateTime (Luxon) complete guide
│ - $jmespath() JSON querying
│ - $getWorkflowStaticData() persistent storage
│ - Standard JavaScript globals
│ - Available Node.js modules
│ - What's NOT available
│
└── README.md (this file)
Skill metadata and overviewTotal: ~2,400 lines across 6 files
---
Coverage
Mode Selection
- Run Once for All Items - Recommended for 95% of use cases
- Run Once for Each Item - Specialized cases only
- Decision guide and performance implications
Data Access
- Most common patterns with usage statistics
- Webhook data structure (critical .body gotcha)
- Safe access patterns with null checks
- When to use which pattern
Error Prevention
- Top 5 errors covering 62%+ of all failures
- Clear wrong vs right examples
- Error prevention checklist
- Debugging tips and console.log usage
Production Patterns
- 10 patterns from real workflows
- Complete working examples
- Use cases and key techniques
- Pattern selection guide
Built-in Functions
- Complete $helpers.httpRequest() reference
- DateTime/Luxon operations (formatting, parsing, arithmetic)
- $jmespath() for JSON queries
- Persistent storage with $getWorkflowStaticData()
- Standard JavaScript and Node.js modules
---
Critical Gotchas Highlighted
#1: Webhook Data Structure
MOST COMMON MISTAKE: Webhook data is under .body
// ❌ WRONG
const name = $json.name;
// ✅ CORRECT
const name = $json.body.name;#2: Return Format
CRITICAL: Must return array with json property
// ❌ WRONG
return {json: {result: 'success'}};
// ✅ CORRECT
return [{json: {result: 'success'}}];#3: Expression Syntax
Don't use `{{}}` in Code nodes
// ❌ WRONG
const value = "{{ $json.field }}";
// ✅ CORRECT
const value = $json.field;---
Integration with Other Skills
n8n Expression Syntax
- Distinction: Expressions use
{{}}in OTHER nodes - Code nodes: Use JavaScript directly (no
{{}}) - When to use each: Code vs expressions decision guide
n8n MCP Tools Expert
- Find Code node:
search_nodes({query: "code"}) - Get configuration:
get_node_essentials("nodes-base.code") - Validate code:
validate_node_operation()
n8n Node Configuration
- Mode selection (All Items vs Each Item)
- Language selection (JavaScript vs Python)
- Understanding property dependencies
n8n Workflow Patterns
- Code nodes in transformation step
- Webhook → Code → API pattern
- Error handling in workflows
n8n Validation Expert
- Validate Code node configuration
- Handle validation errors
- Auto-fix common issues
---
When to Use Code Node
Use Code node when:
- ✅ Complex transformations requiring multiple steps
- ✅ Custom calculations or business logic
- ✅ Recursive operations
- ✅ API response parsing with complex structure
- ✅ Multi-step conditionals
- ✅ Data aggregation across items
Consider other nodes when:
- ❌ Simple field mapping → Use Set node
- ❌ Basic filtering → Use Filter node
- ❌ Simple conditionals → Use IF or Switch node
- ❌ HTTP requests only → Use HTTP Request node
Code node excels at: Complex logic that would require chaining many simple nodes
---
Success Metrics
Before this skill:
- Users confused by mode selection
- Frequent return format errors
- Expression syntax mistakes
- Webhook data access failures
- Missing null check crashes
After this skill:
- Clear mode selection guidance
- Understanding of return format
- JavaScript vs expression distinction
- Correct webhook data access
- Safe null-handling patterns
- Production-ready code patterns
---
Quick Reference
Essential Rules
1. Choose "All Items" mode (recommended) 2. Access data: $input.all(), $input.first(), $input.item 3. MUST return: [{json: {...}}] format 4. Webhook data: Under .body property 5. No `{{}}` syntax: Use JavaScript directly
Most Common Patterns
- Batch processing → $input.all() + map/filter
- Single item → $input.first()
- Aggregation → reduce()
- HTTP requests → $helpers.httpRequest()
- Date handling → DateTime (Luxon)
Error Prevention
- Always return data
- Check for null/undefined
- Use try-catch for risky operations
- Test with empty input
- Use console.log() for debugging
---
Related Documentation
- n8n Code Node Guide: https://docs.n8n.io/code/code-node/
- Built-in Methods Reference: https://docs.n8n.io/code-examples/methods-variables-reference/
- Luxon Documentation: https://moment.github.io/luxon/
---
Evaluations
5 test scenarios covering: 1. Webhook body gotcha (most common mistake) 2. Return format error (missing array wrapper) 3. HTTP request with $helpers.httpRequest() 4. Aggregation pattern with $input.all() 5. Expression syntax confusion (using {{}})
Each evaluation tests skill activation, correct guidance, and reference to appropriate documentation files.
---
Version History
- v1.0 (2025-01-20): Initial implementation
- SKILL.md with comprehensive overview
- DATA_ACCESS.md covering all access patterns
- COMMON_PATTERNS.md with 10 production patterns
- ERROR_PATTERNS.md covering top 5 errors
- BUILTIN_FUNCTIONS.md complete reference
- 5 evaluation scenarios
---
Author
Conceived by Romuald Członkowski - www.aiadvisors.pl/en
Part of the n8n-skills collection.
Related skills
FAQ
Why is my webhook data undefined in an n8n Code node?
Webhook data is nested under the .body property, so read $json.body.name rather than $json.name.
What must a Code node return?
An array of objects each with a json property, for example [{json: {...}}].