| name | writing-n8n-code |
| description | Writes JavaScript and Python code for n8n Code nodes. Covers data access patterns ($input, $json, $node), return format requirements, built-in helpers ($helpers.httpRequest, DateTime, $jmespath), webhook data structure, and common error prevention. Use when writing code inside n8n Code nodes or when the user mentions $input, $json, Code node, or custom JavaScript/Python in workflows. |
Writing Code in n8n Code Nodes
Quick Start
const items = $input.all();
const results = [];
for (const item of items) {
results.push({
json: {
name: item.json.name,
processed: true
}
});
}
return results;
Essential rules:
- MUST return
[{json: {...}}, ...] — array of objects with json key
- Use
$input.all() for batch, $input.first() for single item
- Webhook data is under
$json.body, NOT at root
- No
{{ }} expressions — use plain JavaScript
$helpers.httpRequest() for HTTP calls (not fetch/axios)
Mode Selection
Run Once for All Items (recommended, 95% of cases)
Processes all items in a single execution. Use for: filtering, aggregation, transformation, combining data, generating summaries.
const items = $input.all();
return items.filter(item => item.json.status === 'active')
.map(item => ({ json: { name: item.json.name } }));
Run Once for Each Item (specialized)
Runs your code once per item. Use when: each item needs independent processing, you need $input.item, or items require different error handling.
const data = $input.item.json;
return [{ json: { processed: data.name.toUpperCase() } }];
Data Access Patterns
$input.all() — All items (most common)
const items = $input.all();
const active = items.filter(i => i.json.active);
const names = items.map(i => ({ json: { name: i.json.name.toUpperCase() } }));
const total = items.reduce((sum, i) => sum + i.json.amount, 0);
return [{ json: { total } }];
$input.first() — Single item
const item = $input.first();
const data = item.json;
return [{ json: { id: data.id, name: data.name } }];
$input.item — Current item (Each Item mode only)
const data = $input.item.json;
return [{ json: { result: data.value * 2 } }];
$node["NodeName"] — Reference other nodes
const webhookData = $node["Webhook"].json;
const apiResult = $node["HTTP Request"].json;
const data = $node["My Custom Node"].json;
Webhook Data Structure
Webhook data is nested — the #1 mistake in Code nodes.
const email = $input.first().json.email;
const email = $input.first().json.body.email;
const page = $input.first().json.query.page;
const auth = $input.first().json.headers.authorization;
Return Format
MUST return an array of objects with json key. Everything else causes errors.
return [{ json: { name: "Alice" } }];
return items.map(i => ({ json: { ...i.json, processed: true } }));
return [{ name: "Alice" }];
return { json: { name: "Alice" } };
return [];
Built-in Helpers
$helpers.httpRequest() — HTTP calls
Use this instead of fetch/axios (which are not available).
const response = await $helpers.httpRequest({
method: 'GET',
url: 'https://api.example.com/data',
headers: { 'Authorization': 'Bearer TOKEN' }
});
return [{ json: response }];
const result = await $helpers.httpRequest({
method: 'POST',
url: 'https://api.example.com/submit',
body: { name: 'Alice', email: 'alice@example.com' },
headers: { 'Content-Type': 'application/json' }
});
return [{ json: result }];
try {
const data = await $helpers.httpRequest({ method: 'GET', url: '...' });
return [{ json: data }];
} catch (error) {
return [{ json: { error: error.message } }];
}
DateTime — Date/time operations (Luxon)
const now = DateTime.now();
const today = DateTime.now().startOf('day');
const formatted = DateTime.now().toFormat('yyyy-MM-dd');
const iso = DateTime.now().toISO();
const readable = DateTime.now().toFormat('MMMM d, yyyy');
const date = DateTime.fromISO('2024-01-15');
const fromFormat = DateTime.fromFormat('15/01/2024', 'dd/MM/yyyy');
const tomorrow = DateTime.now().plus({ days: 1 });
const lastWeek = DateTime.now().minus({ weeks: 1 });
const isAfter = date1 > date2;
const diff = date1.diff(date2, 'days').days;
const utc = DateTime.now().toUTC();
const berlin = DateTime.now().setZone('Europe/Berlin');
$jmespath() — JSON querying
const data = $input.first().json;
const names = $jmespath(data, 'users[*].name');
const active = $jmespath(data, 'users[?active==`true`].name');
const first = $jmespath(data, 'users[0].name');
const result = $jmespath(data, 'items[?price > `10`].{name: name, cost: price}');
$getWorkflowStaticData() — Persistent storage
Data persists across workflow executions. Use for: tracking processed IDs, rate limiting, counters.
const staticData = $getWorkflowStaticData('global');
staticData.lastRunTime = DateTime.now().toISO();
staticData.processedIds = staticData.processedIds || [];
const lastRun = staticData.lastRunTime;
staticData.processedIds.push(currentId);
return [{ json: { lastRun, totalProcessed: staticData.processedIds.length } }];
Error Prevention
Error #1: Empty code / missing return (38% of failures)
const items = $input.all();
items.filter(i => i.json.active);
const items = $input.all();
return items.filter(i => i.json.active).map(i => ({ json: i.json }));
Error #2: Expression syntax in Code nodes (8%)
const name = {{ $json.name }};
const name = $input.first().json.name;
Error #3: Wrong return wrapper (5%)
return [{ name: "test" }];
return [{ json: { name: "test" } }];
Error #4: Missing null checks
const email = $input.first().json.body.user.email;
const email = $input.first().json.body?.user?.email ?? 'unknown';
Error #5: Wrong data access in Each Item mode
const items = $input.all();
const data = $input.item.json;
Common Patterns
For complete patterns with production examples, see PATTERNS.md.
Quick reference:
return $input.all()
.filter(i => i.json.status === 'active')
.map(i => ({ json: i.json }));
return $input.all().map(i => ({
json: { ...i.json, fullName: `${i.json.first} ${i.json.last}`, processedAt: DateTime.now().toISO() }
}));
const items = $input.all();
return [{ json: {
count: items.length,
total: items.reduce((s, i) => s + i.json.amount, 0),
names: items.map(i => i.json.name)
}}];
const seen = new Set();
return $input.all().filter(i => {
if (seen.has(i.json.email)) return false;
seen.add(i.json.email);
return true;
}).map(i => ({ json: i.json }));
const groups = {};
for (const item of $input.all()) {
const key = item.json.category;
groups[key] = groups[key] || [];
groups[key].push(item.json);
}
return Object.entries(groups).map(([k, v]) => ({ json: { category: k, items: v, count: v.length } }));
Python in Code Nodes
Use JavaScript for 95% of cases. Python is beta with significant limitations.
Key differences from JavaScript:
_input instead of $input, _json instead of $json
- Must return
[{"json": {...}}] (dict, not object)
- No external libraries — only standard library (json, datetime, re, base64, hashlib, math, random, urllib.parse)
- No
$helpers.httpRequest() equivalent
items = _input.all()
results = []
for item in items:
results.append({"json": {"name": item.json["name"].upper()}})
return results
When to use Python: Only when you specifically need Python's standard library features (regex with re, statistical functions, specific data parsing) and the same cannot be done in JavaScript.
Reference Files
- Complete production patterns (10 patterns with examples): PATTERNS.md