| 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, or choosing between Code node modes. |
| user-invocable | false |
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(), DateTime (Luxon), $jmespath()
When NOT to use a Code node (native-first)
Reach for a native node before writing JavaScript — Code nodes are harder to read, review, and they
run V8 in-process (OOM risk on large data, see below). In a real production audit, Code nodes
outnumbered native conditional nodes ~2.5:1 — much of it avoidable:
| If you are doing… | Use this native node, NOT Code |
|---|
| Branching on a condition | IF (2 branches) / Switch (n branches) |
| Dropping items by a rule | Filter |
| Setting / renaming / mapping fields | Set (Edit Fields) |
| Merging / combining streams | Merge |
| Aggregating into one item | Aggregate / Summarize |
| Looping over large batches | Loop Over Items (SplitInBatches) |
Memory / OOM (real production incident): a Code node that loads or iterates a large dataset —
especially a full DB result set (>~10k rows), binary, or file contents — can spike the V8 heap
(~3 GB cap) and crash the n8n pod (OOM). For large data: page/limit at the source, process in
SplitInBatches, or push the work into the database/native node instead of materializing it all in JS.
Use a Code node when the logic genuinely has no native equivalent (custom parsing, multi-field
computation, shaping a bespoke payload).
Mode Selection Guide
Run Once for All Items (Recommended - Default)
Use for: aggregation, batch processing, transformations that have no native-node equivalent
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 }
}];
Run Once for Each Item
Use for: Specialized cases — per-item validation, independent operations
const item = $input.item;
return [{
json: { ...item.json, processed: true, processedAt: new Date().toISOString() }
}];
Decision: Need to compare multiple items? → All Items. Each item independent? → Each Item. Not sure? → All Items.
Data Access Patterns
const allItems = $input.all();
const firstItem = $input.first();
const data = firstItem.json;
const webhookData = $node["Webhook"].json;
const httpData = $node["HTTP Request"].json;
CRITICAL: Webhook Data Structure
Webhook data is nested under .body:
const name = $json.name;
const name = $json.body.name;
const webhookData = $input.first().json.body;
Return Format Requirements
return [{ json: { field1: value1 } }];
return items.map(item => ({ json: { id: item.json.id, processed: true } }));
return [];
WRONG: return {json: {...}} (no array), return [{field: value}] (no json key)
Built-in Functions
$helpers.httpRequest()
const response = await $helpers.httpRequest({
method: 'GET',
url: 'https://api.example.com/data',
headers: { 'Authorization': 'Bearer token' }
});
return [{ json: { data: response } }];
DateTime (Luxon)
const now = DateTime.now();
const formatted = now.toFormat('yyyy-MM-dd');
const tomorrow = now.plus({ days: 1 });
$jmespath()
const adults = $jmespath(data, 'users[?age >= `18`]');
const names = $jmespath(data, 'users[*].name');
Common Patterns
Aggregation
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 } }];
Filtering + Transformation
return $input.all()
.filter(item => item.json.status === 'active')
.map(item => ({ json: { id: item.json.id, name: item.json.name } }));
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 } }];
}
Top 5 Mistakes
- No return statement → Add
return items.map(item => ({json: item.json}));
- Using
{{ }} in Code node → Use $json.field directly (no braces)
- Wrong return wrapper → Must be
[{json: {...}}], not {json: {...}}
- Missing null checks → Use
item.json?.user?.email || 'fallback'
- Webhook body →
$json.email ❌ → $json.body.email ✅
Best Practices
- Always validate input:
if (!items || items.length === 0) return [];
- Use try-catch for async operations
- Prefer
map/filter over manual loops
- Filter early, process late
- Debug with
console.log()
When to Use Code Node
✅ Complex transformations, custom calculations, API response parsing, multi-step conditionals, data aggregation
❌ Simple field mapping → Set node | Basic filtering → Filter node | HTTP requests → HTTP Request node