Skip to Content

The ten percent no ready-made node can do: the Code node

the escape hatch
n8n Guide · Part 15

The ten percent no ready-made node can do: the Code node

Fourteen parts in, most of what you build is a core node wired to an integration node. Then a job turns up that none of them fit: orders that have to be regrouped along two dimensions at once, an API response buried three levels deep, a top-three ranking by share of revenue. The Code node is the escape hatch n8n gives you for exactly that — a blank JavaScript editor with items going in and items coming out. This part covers the four patterns that handle almost all of it, the two run modes that decide how the code has to be written, and the one output shape that causes more failures than everything else put together.

4 patterns
Filter, map, aggregate, call an API — copy one and change the parameters
2 modes
All Items or Each Item, and the code is written differently for each
99%
How often to pick JavaScript over Python in this node
The ten percent

Everything so far, and the jobs it still cannot reach

By now the shape of a workflow should be familiar. Eight cases out of ten come down to a core node plus a SaaS integration node: pull data, filter it, send a message, write it to a database. The Edit Fields (Set) node reshapes what comes through, expressions fill values in dynamically, and Merge and the splitting nodes put the pieces back together.

Then there is the ten percent. These are the jobs where the ready-made nodes run out, and they are more specific than “something complicated”:

CaseOrders pulled out of a CRM that have to be regrouped along two dimensions at once, customer tier × product category. The Set node cannot do it, because that takes a loop.
CaseAn API response buried three levels deep in { result: { data: { items: [...] } } } when all you want is the items array. One expression can reach it, but add a little more logic and it turns into a mess.
CaseShopify hands you 20 orders and you have to work out what percentage of total revenue the three largest ones account for. That takes sort, slice and reduce together.
CaseTurning 100 items into the nested JSON structure Slack Block Kit expects — piles of { type, elements: [...] }, which would drive you mad in the Set node.

That is what the Code node is for. It gives you a blank JavaScript editor: items go in, you write a few lines, items come out. You do not need to know how to build applications. For most situations, copying one of the four patterns further down this page and changing the parameters is enough.

flowchart LR
  A["Upstream node
hands over an array of items"] --> B["Code node
your JavaScript runs"] B --> C["A new array of items
comes out"] C --> D["Next node
Slack, Sheets, HTTP Request"] E["Ready-made nodes already cover
about eight cases in ten"] -.-> B
Items in, items outThe Code node sits in the chain like any other node and obeys the same contract. What changes is that the box labeled Code node is the one you write yourself. The dotted arrow is a reminder rather than a data path: for most jobs a ready-made node gets there first.
In plain terms

The ready-made nodes are the appliances in a kitchen. The kettle boils water, it does that one job well, and nobody has to be taught how to use it. The Code node is the knife. It can do anything the appliances cannot, and it is also the one thing in the drawer that can cut you — not because it is badly made, but because nothing about it stops you doing the wrong thing.

A warning for anyone who does not write code. Unlike everything up to this point, this part contains real JavaScript. It is completely fine if you cannot read it. In practice, sales and marketing colleagues rarely have to write a Code node themselves, and recognizing that “there is a piece of code here doing X” is enough to work with a workflow that contains one. If you really cannot follow along, skipping to Part 16 on the AI Agent, or back to Part 12 on error handling, costs you nothing later — most business situations never need the Code node at all.
Ask Set first

If Set, IF or Switch can do it, do not open a Code node

The Code node looking capable is not a reason to reach for it. Code is JavaScript, and whoever maintains the workflow later — often you, months on — has to read that code and know JavaScript as well. That bar is a great deal higher than a visual node, so the question to ask before opening one is always whether the built-in nodes could have done the job.

Here is that question worked through for the seven things people most often reach for Code to do.

What you want to doUse this firstWhen to move up to Code
Combine firstName and lastName into fullName The Edit Fields (Set) node — one expression, {{ $json.firstName + ' ' + $json.lastName }}, and it is done. The visual route is the fastest here. No need to move up.
Amount over 1000 takes path A, everything else takes B The IF node — two branches drawn plainly on the canvas, so the logic reads at a glance. No need to move up.
Split into three paths by order status The Switch node — the native node for multi-way branching. No need to move up.
Filter items down to the ones where amount is over 1000 The Filter node, which newer versions have, or an IF node with one output wired. Move up when the condition crosses several fields, or when the condition itself has to be calculated.
Add a field or calculate a new value on every item The Set node plus an expression — that covers most of it. Move up when you need a loop, or when the conditional transformation runs past three or four cases.
Sum all the items, average them, group them by something The Aggregate node, which is built in, or the Summarize node. Move up for a top-N ranking, grouping on several dimensions, or anything statistically involved.
Take a nested API response apart and rebuild the JSON structure The Set node plus expressions usually holds up. Move up only at three levels of nesting or more, or when you need map and reduce.
The test to apply: could three Set nodes do this? If they could, use Set. If they could not, open a Code node. Several Set nodes look like more work, but the next person who opens the workflow understands it at a glance — and that is worth far more than saving two nodes by burying a pile of code inside one.
In plain terms

It is the difference between a route written out with four turns marked and a note that says “you know the way”. The four turns take longer to write down and they take up more room on the page. They are also the only reason somebody else can drive it without ringing you up.

flowchart TD
  A["You need to select,
reshape or summarize data"] --> B{"Could three Set nodes
do this?"} B -->|"yes"| C["Set, IF, Switch,
Filter or Aggregate"] B -->|"no"| D{"Does it need a loop, a sort,
or three levels of nesting?"} D -->|"no"| E["Try the visual nodes first.
Come back if they run out"] D -->|"yes"| F["Open a Code node"] C --> G["Readable by the next person
without knowing JavaScript"]
The decision, before you write a lineOnly the right-hand branch ends in code. Everything else is a node you can already read on the canvas.
JavaScript or Python

Two languages in the dropdown, and one of them to pick

Open a Code node and there is a Language option toward the top right with two choices: JavaScript and Python. In practice, pick JavaScript around 99% of the time. The reasons are worth knowing, because they are not about which language is nicer.

LanguageHow it runsUpsideDownside and verdict
JavaScript
(the default)
Runs in n8n's built-in sandbox. When you self-host you can also switch to task runners, which are separate child processes and the newer architecture n8n recommends. The one n8n leads with. The helper API is complete, there are the most examples, and it runs fast. The default sandbox cannot require external npm packages — you have to enable NODE_FUNCTION_ALLOW_EXTERNAL for that. Pick this by default. Without a specific reason, use JavaScript.
Python From n8n 2 on it uses the native Python task runner, introduced in 1.111.0 and stable in n8n 2. The earlier Pyodide route, which ran Python as WebAssembly, is marked legacy and n8n 2 no longer supports it. You have existing Python logic to move over, and you can pip install the standard library plus third-party packages — only when you self-host and have the task runner set up. Native Python only takes bracket access, _input.item['json'] rather than .json. Whether Cloud has it enabled depends on your plan, and community examples are still mostly JavaScript. Pick it only when you have existing Python logic to port.

“I am better at Python” on its own is not a good reason. The community and the examples are all on the JavaScript side, which means every answer you find when you get stuck will need translating first.

Every remaining example on this page is JavaScript. If you do end up in Python, the syntax differs in two ways worth writing down: variables start with an underscore (_input, _json), and the native Python runner only takes bracket access — _input.item['json']['amount']. The _input.item.json.amount dot syntax that old Pyodide allowed no longer works, which is exactly the line that breaks when somebody copies an old forum answer. When you need the detail, the official page is docs.n8n.io/build/code-in-n8n/using-the-code-node/.

Version wording moves. Language names, dropdown positions and runner arrangements have all changed across n8n releases, and they will change again. Recognize the shape — a language chooser near the top of the node, a mode chooser next to it — rather than the exact word your build prints, and check the official docs when your screen and this page disagree.
Two run modes

All items at once, or one item at a time

At the top of the Parameters panel on the right, next to the Language option, there is a Mode dropdown — the official docs word it “Choose a mode”. The two options behave very differently, and picking the wrong one duplicates or drops data rather than throwing a tidy error.

ModeBehaviorWhen to use itTypical jobs
Run Once for All Items
(the default)
The whole items array comes in at once, your code runs once, and it returns a new items array. When you have to see all the data before you can decide: aggregate, sort, group, rank the top N. “Total the revenue”, “take the top 5 customers”, “group by category”, “merge two upstreams”.
Run Once for Each Item Your code runs once for each item, as if a for loop had already been written around it for you. When a single item is enough to decide, and the transformation rule is the same for every one. “Add tax to each order”, “reformat each message”, “call an API per item to fill in a field”.
In plain terms

It is the difference between totalling the shopping and putting a price sticker on each tin. You cannot total the receipt one tin at a time — you need the whole bag on the counter first. You do not need the whole bag to sticker one tin, and insisting on it just makes the job slower.

The mode changes how the code is written. In All Items mode, use $input.all() to get everything, and return has to hand back an array: [{ json: {} }, ...]. In Each Item mode, use $input.item.json to get the current one, and return hands back a single { json: {} }. Mixing the two throws Items to return were not valid, or produces empty output with no error at all.
flowchart TD
  A["Items arrive at the Code node"] --> B{"Which mode is selected?"}
  B -->|"Run Once for All Items"| C["Your code runs once.
Read every item at once"] C --> D["Return an array of items"] B -->|"Run Once for Each Item"| E["Your code runs once per item.
Read the current item only"] E --> F["Return one single item"] D --> G["Downstream node"] F --> G
One dropdown, two contractsThe left path returns an array, the right path returns one object. Nothing in the editor reminds you which one you are in, so it is worth checking the dropdown before you debug anything else.

Every example below assumes All Items mode unless it says otherwise.

Four patterns to copy

Filter, map, aggregate, call an API

These four cover the overwhelming majority of Code nodes anyone writes. Read them as templates: copy one, change the field names and the conditions, and leave the structure alone.

flowchart LR
  A1["20 items in"] --> B1["filter
a condition removes some"] --> C1["6 items out"] A2["20 items in"] --> B2["map
every item transformed"] --> C2["20 items out"] A3["20 items in"] --> B3["aggregate
reduced to one total"] --> C3["1 item out"] A4["1 item in"] --> B4["call an API
and wait for the answer"] --> C4["1 item with the response"]
Four shapes, by what happens to the countThe quickest way to pick a pattern is to ask how many items should come out compared with how many went in.

Pattern 1 · filter

Pick the items that match a condition out of a pile of them. All Items mode.

// Keep only orders over 1000
return $input.all().filter(item => item.json.amount > 1000);

In plain words, reading it left to right:

  • $input.all() gives you every item that came in, as an array.
  • .filter(item => ...) is a built-in JavaScript array method. It runs that test on every item and keeps only the ones where the answer is true.
  • item.json.amount > 1000 is the test itself. Change this to your own condition and you are done.
  • return ... hands the result to the downstream node.

Swap amount > 1000 for whatever you need — item.json.status === 'paid' and item.json.tags.includes('VIP') both work the same way. Chain several conditions with && for “and” and || for “or”:

// Amount over 1000 and tier is gold
return $input.all().filter(item =>
item.json.amount > 1000 && item.json.tier === 'gold'
);
The Filter node does this too, without any code. Reach for the Code node when the condition crosses several fields, or when the condition itself has to be calculated first. A single comparison on a single field is a job for the visual node.

Pattern 2 · map, to transform each item

Transform every item: add a field, change a field, calculate a new value. All Items mode.

// Combine firstName and lastName into fullName; keep only fullName and age
return $input.all().map(item => ({
json: {
fullName: item.json.firstName + ' ' + item.json.lastName,
age: item.json.age
}
}));
  • .map(item => ({...})) runs that function on every item and returns a new item for each one.
  • { json: {...} } is the format n8n requires for an item. Every item has to be wrapped in a json layer. This one is easy to forget, and forgetting it throws Items to return were not valid.
  • fullName: and age: are the fields you want in the output. Write whichever fields you want to keep.

Notice what that example throws away: anything not listed is gone. If you only want to add a field to the original item rather than rebuild it, use the spread syntax to keep every original field and then add the new ones:

// Keep every original field, then add taxAmount and total
return $input.all().map(item => ({
json: {
...item.json, // spread the original fields
taxAmount: item.json.amount * 0.05,
total: item.json.amount * 1.05
}
}));

The three dots in ...item.json are JavaScript's spread operator — read it as “flatten every field of item.json in right here”. Writing it this way saves you listing every field by hand, and it survives the upstream node gaining a new field next month.

Pattern 3 · aggregate

Shrink a pile of items down to one total. All Items mode.

// Sum every order amount, then work out revenue and item count
const total = $input.all().reduce((sum, item) => sum + item.json.amount, 0);
const count = $input.all().length;

return [{
json: {
total: total,
count: count,
average: total / count
}
}];
  • .reduce((sum, item) => sum + item.json.amount, 0) is JavaScript's reduce, for adding an array up as it goes. sum starts at 0, which is the second argument; each pass adds item.json.amount to it; the total comes back at the end.
  • $input.all().length — the length of the array is the item count.
  • return [{ json: {...} }] — note that it still has to be wrapped in an array, even when you are returning only one item. Forget the outer [] and it blows up.

To group by something rather than totalling everything — a total per customer, say — pair reduce with an object accumulator:

// Total the amounts per customer name
const groups = $input.all().reduce((acc, item) => {
const key = item.json.customer;
acc[key] = (acc[key] || 0) + item.json.amount;
return acc;
}, {});

// turn it back into an items array and return it
return Object.entries(groups).map(([customer, total]) => ({
json: { customer, total }
}));

What that does is sort every order into piles by customer, total each pile, and return one item per customer. It is the most common example of “Set cannot do it, but one block of Code can” — and it is exactly the two-line job that turns into six nodes if you insist on staying visual.

Pattern 4 · call an external API from inside the Code node

This one is rare, and it should stay rare. Inside a Code node you can await a call to an external API directly, but the HTTP Request node is usually easier to maintain — it has a proper interface for headers, authentication and retry, none of which you get for free in code. If you really do need the call inside Code:

// Call an external API and return the response as one item
const response = await this.helpers.httpRequest({
method: 'GET',
url: 'https://api.example.com/x',
headers: { 'Accept': 'application/json' }
});

return [{ json: response }];

this.helpers.httpRequest(...) is a helper n8n builds in — the HTTP Request node, written out in code. Use it when one stretch of logic has to call several APIs in a row, or when a field on an item decides whether to call at all. For a plain single API call, drop in an HTTP Request node instead and keep the credential where the credential belongs.

The URL above is a placeholder. https://api.example.com/x is deliberately not a real endpoint, and there is no key in that snippet either. Keep it that way in your own workflows: put authentication in a credential rather than typing a token into a node parameter, and strip anything sensitive out of a workflow before you export or share its JSON.
The sandbox has limits. this.helpers.httpRequest and the other this.helpers.* functions such as getBinaryDataBuffer work, crypto works in part, and the built-in array, string and JSON methods all work. fs, external require and a bare fetch do not, by default. For npm packages, look up the environment variable NODE_FUNCTION_ALLOW_EXTERNAL in the official docs — self-hosting only, and not possible on Cloud.
The helpers you get

The eleven variables n8n hands you, and the few you will actually use

The Code node comes with a pile of helpers n8n injects for you — the variables that start with $. Ninety percent of the time you only ever touch the first few in this list, but it is worth reading the whole thing once so you know what exists when you need it.

How you write itWhat it isWhich mode it works in
$input.all()The whole array of items this node received.Mainly All Items mode. It works in Each Item too, but that is rare.
$input.itemThe current item — the full object, including json and binary.Only meaningful in Each Item mode.
$input.item.jsonThe json part of the current item, which is what an expression calls $json.Common in Each Item mode.
$('Node Name').all()Grabs every item from a named upstream node. It does not have to be the direct upstream.Works in both modes.
$('Node Name').first() / .last()The first or last item from a named upstream node.Works in both modes.
$nowThe current time, as a Luxon DateTime object.Works in both modes.
$todayToday at 00:00, also Luxon.Works in both modes.
$workflow.id / $workflow.nameThe current workflow's id and name.Works in both modes.
$execution.idThe id of this run. You can look it up afterwards on the Executions page.Works in both modes.
console.log(x)Prints to that node's log tab on the Executions page.Works in both modes — the debugging workhorse.
this.helpers.httpRequest({...})Calls an external API. The equivalent of the HTTP Request node.Works in both modes. Needs await.

Note the quoting style in $('Node Name'): single quotes around the node's name, exactly as the node is labeled on the canvas. Rename a node on the canvas and every $('Node Name') that refers to it has to be updated by hand — nothing rewrites those for you.

The full list, including the more advanced entries, is in the official docs at docs.n8n.io/code/builtin/overview/. That page also covers $jmespath() for JMESPath queries, $max() and $min(), and DateTime for calling the Luxon constructor directly.

When it fails

Four steps, six error messages, and the mistake everyone makes

The Code node throws more errors than any other node type, for the obvious reason that you are writing code in it. The good news is that n8n's error messages are usually clear enough to act on. Work through these four steps in order rather than rereading the code hopefully.

  1. Step 1

    Read the red error message under the node panel

    When a Code node fails, the node itself turns red and an error message appears below it, usually including a line number. SyntaxError at line 3 means go to line 3 and look for an unmatched quote or an unclosed bracket. Do not start by rewriting the logic; start at the line the message named.

  2. Step 2

    Check that node's log on the Executions page

    In the left sidebar go to Executions → click the failed run → click the Code node → switch to the Logs tab at the top. Every console.log(x) you wrote shows up here. Log as much as you like while you are still building; nobody will hold it against you.

  3. Step 3

    Check the output is in the right format

    The Code node ran fine but the downstream node gets nothing? Check whether the output is in the [{ json: {...} }] shape — an array on the outside, every item wrapped in json. Forgetting the json wrapper is the most common mistake of all: a bare return [{ name: 'Elmo' }] makes n8n read every item as empty. The correct form is return [{ json: { name: 'Elmo' } }].

  4. Step 4

    Print nested objects properly

    A plain console.log(item) sometimes prints only [object Object] and tells you nothing. Use console.log(JSON.stringify(x, null, 2)) to turn the object into readable JSON before printing it, and even several layers of nesting become clear at a glance.

In plain terms

Every item n8n passes along travels in an envelope marked json. Hand it a bare sheet of paper and it does not tell you off — it forwards an envelope with nothing inside, and the next node quite honestly reports that it received nothing. That is why “the code ran, the output is empty” is almost always a missing wrapper rather than broken logic.

flowchart TD
  A["The Code node did not do
what you expected"] --> B{"Did the node turn red?"} B -->|"yes"| C["Read the message under the node.
It usually names a line number"] C --> D["Go to that line: unmatched quote,
unclosed bracket, wrong arrow"] B -->|"no"| E{"What did the next node receive?"} E -->|"nothing at all"| F["Check the return statement,
then check the json wrapper"] E -->|"data, but wrong"| G["Open Executions, pick the run,
read that node's Logs tab"] G --> H["Print nested objects as readable JSON
before you log them"]
Check in this orderRed and not-red split the problem in half straight away. The empty-output branch is the one that wastes the most time, because nothing about it looks like an error.
Run it early and often. The debugging loop for a Code node is short: edit the code → click Execute step → read the output and the log → edit again. Do not write a long block before your first run. Run it after two or three lines and you catch the broken line while there is only one candidate. This is exactly how front-end JavaScript gets written.

The six messages you will actually see

What you seeWhat it meansWhat to do
SyntaxError: Unexpected token or Unexpected identifier A plain JavaScript syntax error — usually unmatched quotes, an unclosed bracket, or a stray semicolon. The message gives you the line number; look at that line and the ones either side. Common cases: curly quotation marks mixed in with straight ' ones, which are not valid JavaScript quotes at all; one half of a {} pair missing; => typed as ->.
Cannot read properties of undefined (reading 'xxx') The field you are reaching for has nothing in its parent. item.json.customer.email when there is no customer field, for instance. Three fixes, in increasing order of thoroughness: use optional chaining, item.json.customer?.email; add a guard, if (!item.json.customer) return null;; or fix the upstream node first and confirm it really does return customer.
The output is empty and the downstream node gets nothing Three common causes, and it is worth checking all three rather than guessing. (a) You forgot the return — a function with no return gives back undefined. (b) You forgot the { json: {} } wrapper, so n8n does not recognize your output. (c) The filter condition is too strict and no item matches, so the result is an empty array. That last one is legitimate behavior, but check whether you tightened the condition too far.
Code runs very slowly, past the 30-second timeout Usually a large array — over 10,000 items — running .map or .filter. Put a Split In Batches node upstream and work in batches, feeding 500 to 1000 items into the Code node at a time. Also check the code for a nested loop, a .map with a .filter inside it: that is O(n²), and it blows up as soon as the data grows.
Items to return were not valid What you returned is not in n8n's item format. The correct shape is [{ json: {...} }] — an array on the outside, and every element an object with a json key. Wrong: return { name: 'Elmo' }, which is not an array, and return [{ name: 'Elmo' }], which has no json wrapper. Remember that Each Item mode returns a single { json: {} } while All Items mode returns an array.
ReferenceError: fetch is not defined or require is not defined The Community edition sandbox does not enable fetch or require. Call APIs with await this.helpers.httpRequest({...}) instead. To pull in an npm package, self-hosting is the only route — set the environment variable NODE_FUNCTION_ALLOW_EXTERNAL=lodash to open it up. Not possible on Cloud.

Questions that come up every time

I do not know any JavaScript. Do I have to learn it?
No. Copy the four patterns on this page — filter, map, aggregate, call an API — change the conditions and the field names to your own, and that is about 80% of what you will ever need. In practice, sales and marketing colleagues rarely have to write a Code node themselves. When it does come up, hand the requirement to an engineer or an AI assistant along with an example: “the upstream output looks like this, I want output like this”. You get working code back in ten minutes. It is completely fine if this part goes over your head; jumping straight to Part 16 on the AI Agent costs you nothing later on.
Is Python or JavaScript the better choice?
JavaScript first, for four reasons: (a) n8n leads with JavaScript and its helper API is the most complete; (b) over 90% of community examples are JavaScript; (c) from n8n 2 on, Python runs through the native task runner, so self-hosting means standing one up separately, and the earlier Pyodide/WebAssembly version is legacy and no longer supported in n8n 2; (d) native Python only takes bracket access, _input.item['json']['x'], so watch out when you rewrite a JavaScript example. Pick Python when you have existing Python logic to move over as-is — a string-handling function, say, or a snippet from an internal tool — and you can pip install the packages it needs. Being more comfortable in Python is not on its own a good reason.
Can the Code node use npm packages such as lodash or axios?
On Cloud and on the default Community edition, no — the sandbox only opens the built-in modules such as crypto and querystring. When you self-host, yes, but you have to set the environment variable NODE_FUNCTION_ALLOW_EXTERNAL=lodash,axios on the n8n container, listing the package names you want to open separated by commas, and npm install them into the container. Cloud users who want a similar effect can split the logic into a sub-workflow, as Part 14 covers, or call an outside service with an HTTP Request node instead.
Can two Code nodes share a variable?
No. Every Code node is its own sandbox: a const x = 5 declared in one is invisible in the next. To pass data between nodes, take n8n's regular route — put the value in the output's json, and read it in the next node with $('Previous Code').item.json.x. For a constant shared across a whole workflow, store it in an Edit Fields (Set) node, or use workflow variables — note that $vars is Enterprise only.
Can a Code node await a call to another workflow?
It cannot call another workflow directly — that is the Execute Workflow node's job. What a Code node can await is this.helpers.httpRequest, for calling an external API, and the other built-in helpers that return a Promise. If you want a sub-workflow to run from inside a Code node, restructure it instead: the Code node prepares the data → the Execute Workflow node runs the sub-workflow → the next Code node carries on with the result. The division of labor is clearer that way too.
Can I paste Code node code that an AI assistant generated straight in?
Yes, but verify two things first. (a) That the variables it used are real n8n helpers: a common failure is the AI writing items[0].json, which is the old v0 style, instead of $input.all()[0].json, which is the current one. The old style fails at run time with items is not defined. (b) That the output is wrapped in the { json: {} } format. When you ask for the code, say plainly what you want: “n8n Code node, JavaScript, Run Once for All Items mode, use $input.all()” — the odds of getting it right go up a lot. Run the result over a small amount of data once before you take it live.
What is the difference between the Code node and an expression?
An expression is a dynamic fill-in-the-blank for a single field: one line only, no variable declarations, no loops, no await. The Code node is a whole node: many lines, variables, loops, await, console.log. The test to apply is whether the logic fits inside one {{ }}. If it does, use an expression. If it does not — an if/else with several branches, a for loop, an intermediate value you need to hold on to — use the Code node. Expressions are for filling in values dynamically; the Code node is for handling logic.
How do I know whether my code will run on Cloud?
The fastest method is to run it on Cloud and see. Anything the sandbox blocks throws an error at run time: fetch is not defined, require is not defined, Module not found. As a rule of thumb: (a) all built-in JavaScript syntax is fine — array methods, string methods, JSON, Math, Date; (b) n8n helpers such as $input, $now and this.helpers.httpRequest are fine; (c) npm packages, the fs file system and a bare fetch generally are not. If you are unsure, make a sandbox test workflow and run a few lines to feel out where the edge is.
Next

Where to go from here

keep going

You can write the logic yourself. Next, a node that writes it for you.

Part 16 covers the AI Agent: a node you give a goal and a set of tools rather than a set of steps, and what changes about a workflow when one of its nodes decides for itself what to do next.

Open the full guide

Part 15 of the Woow n8n Onboarding Guide series on the Apporo blog.

Adapted from the Woow n8n Onboarding Guide, produced by WoowTech and released under CC BY 4.0. This adaptation is published by Apporo under the same licence.

Light · Air · Water · Control · apporo

in n8n
The same four nodes in five workflows: pull them into a sub-workflow