TL;DR: Use "Run Once for All Items" mode, read input with $input.all(), and always return an array of { json: {...} } objects. The single mistake that breaks most Code nodes is returning the wrong shape (a bare object, or an array without the json wrapper). The multi-item map below is the pattern you will reuse in nearly every workflow. Python works, but pick JavaScript unless you specifically need a Python standard-library function, because Python has no external libraries and no access to n8n helpers like $helpers.httpRequest().
Download the demo workflow, import it into your own n8n, and run it to see the exact input and output shapes described here.
Copy the workflow JSON below and paste it onto your n8n canvas (Ctrl/Cmd+V) to import it:
{
"name": "demo-code-node",
"nodes": [
{
"parameters": {},
"id": "aa000001-0001-4a10-9f10-000000000001",
"name": "When clicking Test workflow",
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [
-160,
0
]
},
{
"parameters": {
"jsCode": "// Sample input records (normally these come from a trigger,\n// a database node, or an HTTP Request node).\nreturn [\n { json: { first: 'John', last: 'Doe', email: 'john@example.com', amount: 120 } },\n { json: { first: 'Mary', last: 'Smith', email: 'mary@example.com', amount: 340 } },\n { json: { first: 'Sam', last: 'Lee', email: 'sam@example.com', amount: 90 } }\n];\n"
},
"id": "aa000001-0002-4a10-9f10-000000000002",
"name": "Sample records",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
60,
0
]
},
{
"parameters": {
"jsCode": "// Run Once for All Items (default mode).\n// $input.all() returns every incoming item as an array.\n// Each element is an object shaped like { json: {...} }.\n// You MUST return an array where every element is { json: {...} }.\n\nconst items = $input.all();\n\nreturn items.map((item, i) => ({\n json: {\n fullName: `${item.json.first} ${item.json.last}`,\n contactEmail: item.json.email,\n amountWithTax: Math.round(item.json.amount * 1.2 * 100) / 100,\n source: 'n8n-code-node-demo'\n },\n // pairedItem keeps downstream Set / Merge nodes happy\n pairedItem: { item: i }\n}));\n"
},
"id": "aa000001-0003-4a10-9f10-000000000003",
"name": "Transform (multi-item)",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
280,
0
]
}
],
"connections": {
"When clicking Test workflow": {
"main": [
[
{
"node": "Sample records",
"type": "main",
"index": 0
}
]
]
},
"Sample records": {
"main": [
[
{
"node": "Transform (multi-item)",
"type": "main",
"index": 0
}
]
]
}
},
"pinData": {},
"settings": {
"executionOrder": "v1"
},
"meta": {
"templateCredsSetupCompleted": false
}
}
What is the correct return format for the n8n Code node?
Return an array where every element is an object with a json key: [{ json: {...} }]. This holds for both languages and both modes. A single result is still wrapped in an array. Returning a bare object or an array of raw values fails, because the next node expects n8n's item shape.

The reusable multi-item pattern, in JavaScript:
const items = $input.all();
return items.map((item, i) => ({
json: {
fullName: `${item.json.first} ${item.json.last}`,
contactEmail: item.json.email,
source: 'n8n-import'
},
pairedItem: { item: i }
}));
$input.all() gives you every incoming item as an array, each element shaped { json: {...} }. You map over them and return the same shape. Adding pairedItem tells n8n which input each output came from, which prevents paired_item_no_info errors in downstream Set and Merge nodes when your output count differs from your input count.
Running the downloadable workflow above produces this exact output, with the input amounts 120, 340 and 90 each getting a 20% tax added:
[
{ "fullName": "John Doe", "contactEmail": "john@example.com", "amountWithTax": 144, "source": "n8n-code-node-demo" },
{ "fullName": "Mary Smith", "contactEmail": "mary@example.com", "amountWithTax": 408, "source": "n8n-code-node-demo" },
{ "fullName": "Sam Lee", "contactEmail": "sam@example.com", "amountWithTax": 108, "source": "n8n-code-node-demo" }
]
The same transform in Python (Beta mode):
items = _input.all()
return [
{
"json": {
"fullName": f"{item['json']['first']} {item['json']['last']}",
"contactEmail": item["json"]["email"],
"source": "n8n-import"
}
}
for item in items
]
Python swaps $input for _input and uses bracket access on dictionaries. The return contract is identical: a list of dicts, each with a "json" key.
Should I use "Run Once for All Items" or "Run Once for Each Item"?
Use "Run Once for All Items" for roughly 95% of cases. Your code runs a single time, $input.all() holds the whole dataset, and you map, filter, or reduce over it. Switch to "Run Once for Each Item" only when every item is independent and you want per-item logic without a loop; there you read the current item with $input.item.
The practical rule: if you ever need to look at more than one item at once (totals, dedupe, sorting, batching), you must be in All Items mode. Each Item mode cannot see the other items, so aggregation is impossible there.
In Each Item mode the access pattern changes:
// Run Once for Each Item mode
const item = $input.item;
return [{
json: {
...item.json,
processedAt: new Date().toISOString()
}
}];
Note you still return an array even though you are handling one item. Default to All Items and only reach for Each Item when a node genuinely needs isolated per-item execution, such as different error handling per record.
How do I aggregate all items into one result?
Stay in "Run Once for All Items" mode and return a single-element array. You read every item with $input.all(), compute your totals or statistics, then return one { json: {...} } object holding the summary. This collapses many input items into one output item, which is exactly what a reporting or summary step needs.
const items = $input.all();
const amounts = items
.map(item => item.json.amount)
.filter(a => typeof a === 'number');
const total = amounts.reduce((sum, n) => sum + n, 0);
return [{
json: {
count: amounts.length,
total,
average: amounts.length ? total / amounts.length : 0,
max: amounts.length ? Math.max(...amounts) : null
}
}];
The common mistake here is returning one item per input by habit (a .map()), when aggregation needs the opposite: many in, one out. If your downstream node suddenly shows N rows instead of one summary row, you mapped when you should have reduced.
For currency, round at the cent level before comparing. Math.round(value * 100) / 100 avoids floating-point noise that otherwise makes equal prices look different.
How do I filter, transform, and deduplicate items in code?
Filtering and dedupe both run in All Items mode over $input.all(). Filter returns the items that pass a test; dedupe walks the array once with a Set of seen keys. Both return the standard item array, so no shape conversion is needed since you are passing through original items, not building new ones.
Filter on multiple conditions:
return $input.all().filter(item => {
const j = item.json;
const active = j.status === 'active';
const hasEmail = j.email?.includes('@');
return active && hasEmail;
});
Deduplicate by a key:
const seen = new Set();
return $input.all().filter(item => {
const key = item.json.email;
if (seen.has(key)) return false;
seen.add(key);
return true;
});
Because you return the original item objects untouched, their json and pairedItem structure is preserved automatically. You only need to add pairedItem manually when you construct brand-new items, as in the transform example earlier.
Prefer the Filter node for a single simple condition and the Remove Duplicates node for basic dedupe. Reach for the Code node when the logic combines several conditions, references nested fields, or needs a computed key.
What built-in variables and helpers can I call inside the Code node?
JavaScript gives you the most: $input.all(), $input.first(), $input.item, $json for the current item's data, $('Node Name').first().json to pull output from any earlier node, DateTime (Luxon) for dates, $jmespath() for querying JSON, and $helpers.httpRequest() for outbound calls. Python (Beta) exposes _input, _json, _node, _now, and _jmespath(), but no external libraries.
Reference another node's output:
// Correct: call .first() before reading .json
const webhook = $('Webhook').first().json;
A frequent bug is writing $('Webhook').json. A node reference is not an item, so you must call .first() or .all() first. Also remember webhook payloads sit under .body, so it is $('Webhook').first().json.body.email, not .json.email.
Dates with Luxon, in JavaScript:
const now = DateTime.now();
return [{ json: { today: now.toFormat('yyyy-MM-dd') } }];
Query nested JSON without manual loops:
const data = $input.first().json;
const adults = $jmespath(data, 'users[?age >= `18`]');
return [{ json: { adults } }];
Can I call an external API from the Code node?
Yes in JavaScript, using $helpers.httpRequest(), which returns the parsed response so you can wrap it as an item. Do not use it in Python, which has no HTTP helper and cannot import requests. For Python, put an HTTP Request node before your Code node and read its output instead. For anything you call repeatedly, prefer the dedicated HTTP Request node over code, because it is far easier to debug and retry.
const items = $input.all();
const results = [];
for (const item of items) {
const res = await $helpers.httpRequest({
method: 'POST',
url: 'https://api.example.com/enrich',
headers: { 'Content-Type': 'application/json' },
body: { email: item.json.email },
json: true
});
results.push({ json: { ...item.json, ...res } });
}
return results;
Wrap the call in try/catch and return an error item rather than letting the whole run fail, unless you want the workflow to stop. $helpers.httpRequest() handles plain requests; it does not attach stored n8n credentials, so for authenticated third-party APIs the HTTP Request node with a credential is the right tool. See the linked HTTP Request guide below for auth and pagination patterns that belong outside code.
When should I NOT use the Code node?
Skip it whenever a built-in node does the job. Simple field mapping belongs in the Set node, single-condition filtering in the Filter node, branching in IF or Switch, and plain API calls in HTTP Request. The Code node adds logic that is harder to read, test, and hand off. Use it for genuine custom logic: multi-step transforms, cross-item aggregation, computed keys, or parsing awkward nested responses.
The honest tradeoff: a Code node is the most flexible node and the least maintainable one. Every line you write there is a line a teammate has to read to understand the workflow. Push logic into declarative nodes when you can, and keep code for the parts that truly need it.
FAQ
Why does my Code node throw "items is not iterable" or a shape error?
You returned the wrong type. The Code node must return an array, and each element must be { json: {...} }. Returning a bare object, a string, or an array of raw values all fail. Wrap single results as [{ json: {...} }].
Can I import npm packages or Python libraries in the Code node? No, not on standard or cloud installs. JavaScript has built-in Node modules plus n8n helpers; Python has the standard library only (json, datetime, re, hashlib, statistics, and similar) with no requests, pandas, or numpy. External libraries require self-hosting and configuration changes.
Why is my webhook data undefined in the Code node?
Webhook payloads are nested under body. Use $json.body.email in JavaScript or _json["body"]["email"] in Python, not $json.email. Query params and POST fields both live under body.
How do I get all items when the Code node only sees the first one?
You are likely in "Run Once for Each Item" mode, which processes items one at a time and cannot see the others. Switch to "Run Once for All Items" so $input.all() returns the full array.
Should I write JavaScript or Python in n8n?
JavaScript for about 95% of cases. It has the full helper set ($helpers.httpRequest(), DateTime, $jmespath()), more community examples, and matches n8n's expression language. Choose Python only when you specifically need a Python standard-library function and no external dependency.
How do I reference output from an earlier node inside code?
Use $('Node Name').first().json for a single item or $('Node Name').all() for the array. Calling .json directly on the node reference fails; it is not an item until you call .first() or .all().
Need custom logic built and maintained properly? n8n Logic designs n8n workflows that keep code where it belongs and lean on declarative nodes everywhere else. See related guides on the HTTP Request node auth and pagination, running Python in n8n, Webhook node triggers and auth, and environment variables.