n8n-code-javascript
Write JavaScript code in n8n Code nodes. Use when writing JavaScript in n8n, using $input/$json/$node syntax, making HTTP requests with this.helpers / the $helpers global, working with dates using DateTime, troubleshooting Code node errors, choosing between Code node modes, or doing any custom data
By czlonkowski · 6,371 installs
npx skills add czlonkowski/n8n-skills --skill n8n-code-javascript
Source repository · Upstream listing
JavaScript Code Node
Expert guidance for writing JavaScript code in n8n Code nodes.
Before you write a Code node
A new Code node has to pass this gate first:
1. Is there a native node for the job? Check the job to node table in using n8n mcp skills : Filter, IF / Switch,
Summarize, Aggregate, Split Out, Merge, Compare Datasets, Remove Duplicates, Sort + Limit, Date & Time, XML / HTML /
Extract From File / Convert To File, HTTP Request (pagination, batching) and Crypto cover most reaches for Code.
Their configs are in n8n node configuration → NATIVE NODES.md.
2. Would a short expression in that node do? One expression, no function body — the helper methods ( .pluck() ,
.sum() , .unique() , $ifEmpty() , .diffTo() ) are in n8n expression syntax and, on n8n mcp 2.97.0+, in
tools documentation({topic: "expression helpers"}) . Several statements inside {{ }} count as code, so they belong
here rather than in an Edit Fields field.
3. Is the job on the closed list? Loops or recursion no node expresses, state across runs
( $getWorkflowStaticData ), parsing no node handles, an allowlisted library, heavy all items work (thousands of items,
stated or measured), or one short Code node (~15 lines) replacing 4+ glue nodes that only reshape data.
If it passes, write the Code node and give it a note, so the next reader (and the review hooks) can see why:
Editing or debugging a Code node that is already there doesn't need the gate, but if the node does a native node job,
say so and offer the native replacement.
Quick Start
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. Return [{json: {...}}] — the canonical, mode portable form. In Run Once for All Items mode n8n also auto wraps a bare return {…} object, so that runs too; what genuinely fails is returning a primitive (string/number) or null .
4. CRITICAL : Webhook data is under $json.body (not $json directly)
5. Built ins available : this.helpers.httpRequest() (no auth — the bare $helpers global is undefined in the task runner sandbox, so $helpers.httpRequest() throws ReferenceError: $helpers is not defined ), DateTime (Luxon), $jmespath(). Not available : this.helpers.httpRequestWithAuthentication (deny listed), $env (when N8N BLOCK ENV ACCESS IN NODE=true), require() (unless allowlisted). For anything beyond a trivial unauthenticated GET (auth, pagination, retries), prefer the HTTP Request node and keep Code nodes for pure logic.
6. Instance allowlisted libraries : Self hosted instances can allowlist modules via N8N RUNNERS ALLOWED BUILT IN MODULES and N8N RUNNERS ALLOWED EXTERNAL MODULES (legacy: NODE FUNCTION ALLOW BUILTIN / NODE FUNCTION ALLOW EXTERNAL ). If the user says their instance allows specific modules (e.g. axios , lodash , crypto ), use them via require() — don't refuse. If unsure, ask or default to built ins only.
7. Wrong skill? If you're writing code for a Custom Code Tool attached to an AI Agent ( @n8n/n8n nodes langchain.toolCode ), stop — that node has a different contract (input via query , must return a string, no $input / $helpers ). Use the n8n code tool skill.
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 : logic that needs every item at once and passed the gate above
Performance : Faster for multiple items (single execution)
When to use:
✅ Each item depends on earlier items (running totals, sessionizing events, gap detection)
✅ Multi pass algorithms or recursion over nested data of unknown depth
✅ Heavy transforms over thousands of items where one node beats a long chain (measured)
Native first: totals, averages and counts → Summarize ; sorting or ranking → Sort + Limit ; deduplication →
Remove Duplicates ; items into one array → Aggregate ; joining two lists → Merge .
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)
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)
Why "All Items" is faster — the per item boundary
Mode choice is the single biggest performance lever in a Code node. Each per item execution context costs a setup tax (measured on n8n 2.x, small records):
What runs per item Approx. cost
Code All Items (one run for the whole set) ~0.02 ms/item
Expression in any node (IF / Set / etc.) ~0.2 ms/item
Code Each Item (a full sandbox per item) ~0.6 ms/item — ~25–30× All Items
So Run Once for Each Item over 10k items is ~6 s of pure overhead vs ~0.2 s in Run Once for All Items . Use Each Item only when an item genuinely needs isolating (independent error handling, or a per item API call you can't batch); otherwise loop inside one All Items node. Expression complexity itself is essentially free (~90% of the cost is the per item context, not your code) and every node→node hop re copies all items — so reduce the number of per item boundaries, don't micro optimize each one.
On top of the per item cost, every Code node run pays a fixed hand off to the task runner. In the default internal mode the runner stops after 15 s idle ( N8N RUNNERS AUTO SHUTDOWN TIMEOUT ), so a workflow that runs now and then pays a cold start of hundreds of milliseconds on most runs. Native transform nodes process all items in one in process call and skip that hand off.
What this means for node choice. Most workflows are I/O bound, and below a few hundred items none of these costs matter: choose by readability, which means native nodes first. Performance becomes the deciding argument only for heavy all items work — thousands of items per run, transform work rather than I/O dominating, stated by the user or measured. Then one All Items Code node can beat a long chain of nodes, and the reason goes in its Why Code: note. These figures are the pack's one measured reference; the other skills point here instead of repeating them.
See : [DATA ACCESS.md](DATA ACCESS.md) → "Mode Performance" for the corollaries, hop costs, and scale check.
Data Access Patterns
Four ways to pull data from upstream nodes. Note $node["Name"] and $('Name') need .first().json or .all() — never .json directly.
Always access fields via .json (e.g. item.json.name , not item.name ), and prefer the explicit $input.first().json.field over a bare $json.field .
See : [DATA ACCESS.md](DATA ACCESS.md) for the full guide — every pattern with examples, a decision tree, and the common mistakes (mutating originals, missing length checks, $input.item in the wrong mode).
Critical: Webhook Data Structure
MOST COMMON MISTAKE : Webhook data is nested under .body
Why : Webhook node wraps all request data under body property. This includes POST data, query parameters, and JSON payloads.
See : [DATA ACCESS.md](DATA ACCESS.md) for full webhook structure details
Return Format Requirements
Canonical form : [{json: {...}}] — an array of objects each with a json property. It is unambiguous and works identically in both execution modes, so make it your default.
In Run Once for All Items mode n8n auto normalizes looser shapes on the way out: a single bare object, or an array of bare objects, gets wrapped under json for you. So return {foo: 1} runs. What has nothing to wrap — and therefore genuinely fails at runtime with "Code doesn't return items properly" — is a primitive (string/number/boolean) or null / undefined . (n8n mcp ≥ 2.63.0 no longer flags a bare object return as an error; it reflects this auto wrap behavior.)
Correct Return Formats
Non Canonical Returns (auto wrapped — prefer the canonical form)
Genuinely Broken Returns
Why it matters : The canonical [{json: {...}}] is unambiguous and behaves the same in both modes. n8n auto normalizes bare objects and arrays of objects in All Items mode, but a primitive or null return has nothing to wrap and stops execution.
See : [ERROR PATTERNS.md](ERROR PATTERNS.md) 3 for detailed error solutions
Common Patterns Overview
The most useful Code node shapes from production workflows, for when Code passed the gate. Several of them have a native equivalent (totals → Summarize, top N → Sort + Limit, JSON comparison → Compare Datasets, string aggregation → Aggregate or Summarize concatenate ); each pattern in the library opens with its native alternative. The full library covers 10 patterns: multi source aggregation, regex filtering, markdown/structured text parsing, JSON comparison, CRM/form transformation, release processing, array transformation with computed fields, Slack Block Kit formatting, top N ranking, and string aggregation reporting — each with variations.
See : [COMMON PATTERNS.md](COMMON PATTERNS.md) for the 10 detailed production patterns (and the Best Practices section: validate input, try catch, filter early, array methods over loops, console.log debugging).
Error Prevention Top Mistakes
The recurring Code node failures, in rough frequency order:
1. Empty code / missing return — always end with return [...] , and make sure every branch returns.
2. Expression syntax as code — don't write {{ }} where JavaScript belongs ( return {{ $json.x }} is a syntax error). Use ${$json.field} or $input.first().json.field . {{ }} inside a string literal is fine — it's just literal text n8n won't evaluate.
3. Return shape — prefer return [{json:{...}}] . A bare return {…} auto wraps in All Items mode, but returning a primitive (string/number) or null is what actually fails.
4. Missing null checks — use optional chaining: item.json?.user?.email 'fallback' .
5. Webhook body nesting — $json.email is undefined; use $json.body.email .
6. Auth helpers blocked ( httpRequestWithAuthentication ) and $env blocked — route secrets through credentials/HTTP Request node, not the Code node sandbox.
See : [ERROR PATTERNS.md](ERROR PATTERNS.md) for the comprehensive guide — each error with wrong/right code, escaping rules, the sandbox restrictions (Errors 6– 7), a prevention checklist, and a quick error message lookup table.
Built in Functions & Helpers
Sandbox (since n8n v2.0, JsTaskRunnerSandbox): the accessor is this.helpers.httpRequest() — the bare $helpers global is undefined here ( $helpers.httpRequest() throws ReferenceError ). Inside a nested async function where this is lost, call it as await fn.call(this, ...) . this.helpers.httpRequestWithAuthentication and this.helpers.requestWithAuthenticationPaginated are deny listed (→ UnsupportedFunctionError ); for authenticated calls use an HTTP Request node with the credential (preferred), a sub workflow, or a manual Authorization: Bearer ${token} header on this.helpers.httpRequest() only when the token already flows through the workflow as data. $env is blocked when N8N BLOCK ENV ACCESS IN NODE=true ; require() works only for allowlisted modules. Buffer , URL , and standard JS globals (Math, JS