| name | n8n-code-javascript |
| description | Write JavaScript code in n8n Code nodes. Use when writing JavaScript in n8n, using $input/$json/$node syntax, making HTTP requests with $helpers, working with dates using DateTime, troubleshooting Code node errors, choosing between Code node modes, or doing any custom data transformation in n8n. Always use this skill when a workflow needs a Code node — whether for data aggregation, filtering, API calls, format conversion, batch processing logic, or any custom JavaScript. Covers SplitInBatches loop patterns, cross-iteration data, pairedItem, and real-world production patterns. |
JavaScript Code Node
Expert guidance for writing JavaScript code in n8n Code nodes.
Quick Start
const items = $input.all();
const processed = items.map(item => ({
json: {
...item.json,
processed: true,
timestamp: new Date().toISOString()
}
}));
return processed;
Essential Rules
- Choose "Run Once for All Items" mode (recommended for most use cases)
- Access data:
$input.all(), $input.first(), or $input.item
- CRITICAL: Must return
[{json: {...}}] format
- CRITICAL: Webhook data is under
$json.body (not $json directly)
- Built-ins available: $helpers.httpRequest() (no auth), DateTime (Luxon), $jmespath(). Not available: $helpers.httpRequestWithAuthentication, $env (when N8N_BLOCK_ENV_ACCESS_IN_NODE=true), require() (unless allowlisted)
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() or items array
- Best for: Aggregation, filtering, batch processing, transformations, API calls with all data
- Performance: Faster for multiple items (single execution)
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.item or $item
- Best for: Item-specific logic, independent operations, per-item validation
- Performance: Slower for large datasets (multiple executions)
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
const allItems = $input.all();
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
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
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
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
const name = $json.name;
const email = $json.email;
const name = $json.body.name;
const email = $json.body.email;
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
return [{
json: {
field1: value1,
field2: value2
}
}];
return [
{json: {id: 1, data: 'first'}},
{json: {id: 2, data: 'second'}}
];
const transformed = $input.all()
.filter(item => item.json.valid)
.map(item => ({
json: {
id: item.json.id,
processed: true
}
}));
return transformed;
return [];
if (shouldProcess) {
return [{json: processedData}];
} else {
return [];
}
Incorrect Return Formats
return {
json: {field: value}
};
return [{field: value}];
return "processed";
return $input.all();
return [{data: 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';
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)
const items = $input.all();
const items = $input.all();
return items.map(item => ({json: item.json}));
#2: Expression Syntax Confusion
const value = "{{ $json.field }}";
const value = `${$json.field}`;
const value = $input.first().json.field;
#3: Incorrect Return Wrapper
return {json: {result: 'success'}};
return [{json: {result: 'success'}}];
#4: Missing Null Checks
const value = item.json.user.email;
const value = item.json?.user?.email || 'no-email@example.com';
if (!item.json.user) {
return [];
}
const value = item.json.user.email;
#5: Webhook Body Nesting
const email = $json.email;
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:
const now = DateTime.now();
const formatted = now.toFormat('yyyy-MM-dd');
const iso = now.toISO();
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;
const adults = $jmespath(data, 'users[?age >= `18`]');
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();
if (!items || items.length === 0) {
return [];
}
if (!items[0].json) {
return [{json: {error: 'Invalid input format'}}];
}
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
const processed = $input.all()
.filter(item => item.json.valid)
.map(item => ({json: {id: item.json.id}}));
const processed = [];
for (const item of $input.all()) {
if (item.json.valid) {
processed.push({json: {id: item.json.id}});
}
}
4. Filter Early, Process Late
const processed = $input.all()
.filter(item => item.json.status === 'active')
.map(item => expensiveTransformation(item));
const processed = $input.all()
.map(item => expensiveTransformation(item))
.filter(item => item.json.status === 'active');
5. Use Descriptive Variable Names
const activeUsers = $input.all().filter(item => item.json.active);
const totalRevenue = activeUsers.reduce((sum, user) => sum + user.json.revenue, 0);
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()
const items = $input.all();
console.log(`Processing ${items.length} items`);
for (const item of items) {
console.log('Item data:', item.json);
}
return result;
Production Gotchas
Hard-won lessons from real-world n8n workflow deployments:
SplitInBatches Loop Semantics
The SplitInBatches node has two outputs — and the naming is counterintuitive:
main[0] = done — fires ONCE after all batches are processed
main[1] = each batch — fires for every batch (this is the loop body)
Always add a Limit 1 node after the done output before downstream processing, as a safety against edge cases where done fires with extra items.
Cross-Iteration Data Accumulation (CRITICAL)
After a SplitInBatches loop, $('Node Inside Loop').all() returns ONLY the last iteration's items, not cumulative results. This silently drops data from all but the final batch.
Fix: Use workflow static data to accumulate across iterations:
const staticData = $getWorkflowStaticData('global');
staticData.results = [];
return $input.all();
const staticData = $getWorkflowStaticData('global');
const results = [];
for (const item of $input.all()) {
const processed = { };
results.push({ json: processed });
staticData.results.push(processed);
}
return results;
const staticData = $getWorkflowStaticData('global');
const allResults = staticData.results || [];
pairedItem for New Output Items
When creating new items that don't map 1:1 to input items, include pairedItem — otherwise downstream Set nodes fail with paired_item_no_info:
const results = [];
for (let i = 0; i < $input.all().length; i++) {
const item = $input.all()[i];
results.push({
json: { },
pairedItem: { item: i }
});
}
return results;
Correct Node Reference Syntax
const data = $('HTTP Request').json;
const data = $('HTTP Request').first().json;
const allData = $('HTTP Request').all();
Float Precision for Price/Currency Comparison
When comparing prices or currency values, floating point noise can cause false positives. Round to cents:
if (newPrice !== oldPrice) { }
if (Math.round(newPrice * 100) !== Math.round(oldPrice * 100)) {
}
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({nodeType: "nodes-base.code"})
- Validate code:
validate_node({nodeType: "nodes-base.code", config: {...}})
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:
Additional Resources
Related Files
n8n Documentation
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.