Code node:在 workflow 裡寫幾行 JavaScript
現成節點做得到 90% 的事,剩下 10% 的怪需求——複雜資料轉換、多來源合併邏輯、動態產生內容——n8n 給你一個逃生口:Code node。這章教你在 workflow 裡塞幾行 JavaScript 補洞。新手警告:這章有 JS,看不懂完全沒關係,直接 copy 我給的 pattern 改參數就好;真的跟不上,直接跳到第 21 章 AI Agent 也完全 OK,多數業務場景根本用不到 Code node。
為什麼要 Code node
前面 19 章講完,你會發現八成情境用 core node 加 SaaS 整合節點 就搞得定:拉資料、過濾、發訊息、寫資料庫。Set 節點幫你整形,Expression 幫你動態填空,Merge/Split 幫你重組。
但總有那 10% 現成節點做不到的怪需求:
- 從 CRM 拉出來的訂單,要按「客戶等級 × 商品類別」兩個維度重新分組——Set 節點做不到,因為那要跑 loop。
- API 回來的資料塞在
{ result: { data: { items: [...] } } }巢狀三層,你只要 items 陣列——Expression 一行寫得出來,但邏輯稍微多一點就變一坨。 - Shopify 給你 20 筆訂單,你要算「金額前三大訂單佔總營收幾 %」——這需要 sort、slice、reduce 一起用。
- 要把 100 筆資料轉成 Slack Block Kit 的 JSON 結構——一堆巢狀
{ type, elements: [...] },用 Set 節點會瘋掉。
這時候就是 Code node 上場。它給你一個空白 JavaScript 編輯器,讓你寫幾行 code 把 items 進來、items 出去。不需要你會寫應用程式——大多場景抄下面四個 pattern 改參數就夠用。
先問:能用 Set 就別用 Code
看到 Code node 很酷不代表要濫用。能用 Set/IF/Switch 解決的,就別開 Code node——因為 Code node 用 JavaScript,日後別人(或未來的你)維護時要重讀 code、還要懂 JS,門檻比視覺化節點高很多。
| 你要做的事 | 優先用 | 什麼時候升級到 Code |
|---|---|---|
把 firstName 跟 lastName 合成 fullName |
Set / Edit Fields 節點——用 Expression {{ $json.firstName + ' ' + $json.lastName }} 一行搞定,視覺化最快。 |
不需要升級。 |
| 金額 > 1000 才走 A 路徑,否則走 B | IF 節點——兩條分支明確畫在畫布上,一眼看懂邏輯。 | 不需要升級。 |
| 按「訂單狀態」分成三個路徑 | Switch 節點——多路分支的原生節點。 | 不需要升級。 |
| 把 items 過濾成「金額 > 1000」的那幾筆 | Filter 節點(新版有這個),或 IF 節點單邊接。 | 條件牽扯多個欄位交叉判斷、或要動態算條件,升 Code。 |
| 對每筆 item 做「加欄位、算新值」 | Set 節點 + Expression——大多能搞定。 | 要跑 loop、或條件轉換超過 3-4 個 case 時升 Code。 |
| 把所有 items 加總、算平均、group by | Aggregate 節點(有內建),或 Summarize 節點。 | 要算「前 N 名」、多維度分組、比較複雜的統計時升 Code。 |
| 把巢狀 API response 拆解、重新組合 JSON 結構 | Set 節點 加 Expression 通常撐得住。 | 三層以上巢狀、或需要跑 map/reduce 才升 Code。 |
Code node 有兩種語言可以選
打開 Code node,右上角 Language 有兩個選項:JavaScript 跟 Python。實務上 99% 情況選 JavaScript,理由如下。
| 語言 | 怎麼跑 | 優點 | 缺點 | 建議 |
|---|---|---|---|---|
| JavaScript(預設) | 透過 n8n 內建 sandbox 執行;自架時也可切到task runners(獨立子行程,較新的官方推薦架構)。 | 官方主打;helper API 完整;範例最多;跑得快。 | 預設 sandbox 不能 require 外部 npm 套件(要開 NODE_FUNCTION_ALLOW_EXTERNAL)。 |
預設選這個。沒有特別理由就用 JS。 |
| Python | n8n 2 起改用 native Python task runner(1.111.0 引進、n8n 2 stable);早期用的 Pyodide(WebAssembly)已標為 legacy、n8n 2 不再支援。 | 有 Python 現成邏輯要搬;能 pip install 標準庫 + 第三方套件(自架設好 task runner 才行)。 |
native Python 只吃括號存取(_input.item['json'],不能寫 .json);Cloud 版是否啟用要看方案;社群範例仍以 JS 為主。 |
只在有現成 Python 邏輯要移植時才選。純粹「我比較會 Python」不是好理由——因為社群跟範例都在 JS 那邊。 |
本章接下來所有範例都用 JavaScript。Python 語法差在:變數改成底線開頭(_input、_json),而且 native Python runner 只吃括號存取(_input.item['json']['amount'])——舊 Pyodide 允許的 _input.item.json.amount 點語法已不能用。需要時參考官方文件 docs.n8n.io/build/code-in-n8n/using-the-code-node/。
兩種執行模式:一次全部 vs 逐筆
打開 Code node,右側 Parameters 面板最上方(在 Language 選項旁)有一個 Mode 下拉——官方文件的原話是「Choose a mode」。兩個選項差很大,選錯會造成資料重複或漏掉。
| Mode | 行為 | 什麼時候用 | 常見場景 |
|---|---|---|---|
| Run Once for All Items(預設) | 整份 items 陣列一次進來,你的 code 跑一次,吐出新的 items 陣列。 | 要看到全部資料才能做決定:聚合、排序、group、算前 N 名。 | 「算總營收」「取前 5 大客戶」「按類別 group」「合併兩個上游」。 |
| Run Once for Each Item | 對每個 item 各跑一次你的 code,就像 for loop 幫你跑好。 | 只需看單筆資料就能決定,且轉換規則對每筆都一樣。 | 「幫每筆訂單加 tax」「每筆訊息重新格式化」「單筆呼叫 API 補資料」。 |
- All Items:用
$input.all()拿全部;return要回傳陣列[{ json: {} }, ...]。 - Each Item:用
$input.item.json拿當前那筆;return回一個{ json: {} }。
Items to return were not valid 或吐出空 output。本章範例默認 All Items 模式,除非特別註明。
Pattern 1:過濾(filter)
從一堆 items 挑出符合條件的那幾筆。All Items 模式。
// 只留下金額大於 1000 的訂單
return $input.all().filter(item => item.json.amount > 1000);
白話拆解:
$input.all():拿到全部進來的 items(陣列)。.filter(item => ...):JavaScript 陣列的內建方法,對每筆跑那個判斷式,只留下判斷是true的。item.json.amount > 1000:判斷式本體。把這裡改成你的條件就好。return ...:吐回去給下游。
Copy 這段、把 amount > 1000 換成你要的條件(item.json.status === 'paid'、item.json.tags.includes('VIP') 都可以),就完成了。多條件用 &&(且)跟 ||(或)串:
// 金額大於 1000 且客戶等級是 gold
return $input.all().filter(item =>
item.json.amount > 1000 && item.json.tier === 'gold'
);
Pattern 2:映射(map / 轉換每筆)
對每筆 item 做轉換:加欄位、改欄位、算新值。All Items 模式。
// 把 firstName + lastName 合成 fullName,只保留 fullName 跟 age
return $input.all().map(item => ({
json: {
fullName: item.json.firstName + ' ' + item.json.lastName,
age: item.json.age
}
}));
白話拆解:
.map(item => ({...})):對每筆 item 跑那個函式,回傳「新的 item」。{ json: {...} }:n8n item 的規定格式——每個 item 都要包一層json。這個很容易忘,忘了就會噴Items to return were not valid。fullName: ...、age: ...:你想輸出的欄位。想加什麼欄位就寫什麼。
如果你只是要在原本 item 上「加一個欄位」而不是重建,用 spread 語法 ...item.json 保留原本所有欄位再加新的:
// 保留原本所有欄位,再多加 taxAmount 跟 total
return $input.all().map(item => ({
json: {
...item.json, // 展開原本欄位
taxAmount: item.json.amount * 0.05,
total: item.json.amount * 1.05
}
}));
...item.json 三個點是 JavaScript 的 spread——「把 item.json 所有欄位攤平在這裡」。這種寫法可以避免手動列所有欄位。
Pattern 3:聚合(aggregate)
把一堆 items 縮成一筆總計。All Items 模式。
// 把所有訂單金額加總,算出總營收跟筆數
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):JavaScript 的 reduce,用來「把陣列一路加起來」。sum從0開始(第二個參數),每次把item.json.amount加上去,最後回傳總和。$input.all().length:陣列長度就是筆數。return [{ json: {...} }]:注意還是要包成陣列,就算只吐一筆——[{ json: {} }]。忘了外層[]就會爆。
要 group by(例:按客戶分別加總),用 reduce 搭配物件累積:
// 按客戶名字加總金額
const groups = $input.all().reduce((acc, item) => {
const key = item.json.customer;
acc[key] = (acc[key] || 0) + item.json.amount;
return acc;
}, {});
// 轉成 items 陣列吐出去
return Object.entries(groups).map(([customer, total]) => ({
json: { customer, total }
}));
這段做的事:把所有訂單按 customer 分堆、每堆加總、最後每個客戶吐一筆。這是 Code node 最常見的「Set 做不到但 Code 一段就搞定」的例子。
Pattern 4:從 Code node 呼叫外部 API(很少用)
Code node 內可以用 await 直接呼叫外部 API,但通常用 HTTP Request 節點更好維護——它有 UI 幫你設 headers、auth、retry。真的要在 Code 內呼叫的話:
// 呼叫外部 API,把回應塞成一筆 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(...) 是 n8n 內建的 helper,等同 HTTP Request 節點但寫在 code 裡。使用時機:要在同一段邏輯裡連續呼叫 N 個 API、或依照某筆資料的欄位動態決定要不要打 API。純粹「呼叫一次 API」的話拉個 HTTP Request 節點就好。
this.helpers.httpRequest(跟其他 this.helpers.* 例如 getBinaryDataBuffer)、crypto(部分)、內建陣列/字串/JSON 方法都可以;fs、外部 require、直接 fetch 預設不行。要用 npm 套件請看官方文件的環境變數 NODE_FUNCTION_ALLOW_EXTERNAL(僅 self-host 有效,Cloud 無法)。常用 helper API 速查
Code node 有一堆 n8n 幫你注入的 helper($ 開頭的變數)。90% 時候只會用到下面幾個。
| 寫法 | 是什麼 | 能不能在哪個 mode 用 |
|---|---|---|
$input.all() |
當前節點吃到的全部 items 陣列。 | All Items 模式主用;Each Item 也能用但少見。 |
$input.item |
當前這筆 item(完整物件,含 json 跟 binary)。 |
只在 Each Item 模式有意義。 |
$input.item.json |
當前 item 的 json 部分(=Expression 裡的 $json)。 |
Each Item 模式常用。 |
$('Node Name').all() |
抓指定上游節點的全部 items(不限直接上游)。 | 兩個 mode 都能用。 |
$('Node Name').first() / .last() |
抓指定上游節點的第一筆 / 最後一筆。 | 兩個 mode 都能用。 |
$now |
當前時間(Luxon DateTime 物件)。 | 兩個 mode 都能用。 |
$today |
今天 00:00(Luxon)。 | 兩個 mode 都能用。 |
$workflow.id / $workflow.name |
當前 workflow 的 id 跟名稱。 | 兩個 mode 都能用。 |
$execution.id |
當前這次執行的 id(Executions 頁面能查)。 | 兩個 mode 都能用。 |
console.log(x) |
印到Executions 頁面該節點的 log 分頁。 | 兩個 mode 都能用(debug 神器)。 |
this.helpers.httpRequest({...}) |
呼叫外部 API(等同 HTTP Request 節點)。 | 兩個 mode 都能用,需 await。 |
完整清單去官方文件 docs.n8n.io/code/builtin/overview/,還有 $jmespath()(JMESPath 查詢)、$max()/$min()、DateTime(直接叫 Luxon 建構器)這些進階的。
Code node 出錯怎麼辦
Code node 是最容易噴 error 的節點類型,因為你在寫程式碼。好消息是 n8n 的 error 訊息通常寫得夠清楚——照下面 4 步走。
-
看節點面板下方的紅色 error 訊息
Code node 執行失敗時,節點本身會變紅、下方會顯示 error 訊息,通常包含行號。例:
SyntaxError at line 3——直接跳到第 3 行看是不是引號沒對、括號沒閉。 -
去 Executions 頁面看該節點的 log
左側選單 Executions → 點失敗那筆 → 點 Code node → 上方切到 Logs 分頁。你在 code 裡打的
console.log(x)全在這裡看得到。dev 期間狂 log 沒關係,沒人會怪你。 -
檢查 output 是不是正確格式
Code node 成功執行但下游拿不到資料?看 output 是不是
[{ json: {...} }]這個格式——外層要陣列、每筆要包json。忘了包json是最常見的錯,例:直接return [{ name: 'Elmo' }]會被 n8n 認為每筆的資料是空的。正確:return [{ json: { name: 'Elmo' } }]。 -
用
console.log(JSON.stringify(x, null, 2))印巢狀物件直接
console.log(item)有時只印[object Object]看不到內容,用JSON.stringify(item, null, 2)把物件轉成好讀的 JSON 字串再印,巢狀多層也一目了然。
常見卡關
-
SyntaxError: Unexpected token或Unexpected identifierJavaScript 語法錯,通常是引號不對稱、括號沒閉、分號亂放。error 訊息會告訴你行號,跳過去看那行前後。常見:混用中文引號
「」跟英文''(前者不是有效 JS 引號);花括號{}少一邊;=>打成->。 -
Cannot read properties of undefined (reading 'xxx')你要抓的欄位它的父層根本沒東西。例:
item.json.customer.email,但customer這個欄位不存在。解法:(a)用 optional chainingitem.json.customer?.email;(b)加 if 判斷if (!item.json.customer) return null;;(c)先修上游節點,確認它真的回傳customer。 -
Output 空的,下游拿不到資料
三個常見原因:(a)忘了寫
return——function 沒 return 等於回undefined;(b)忘了包{ json: {} }——n8n 認不出你的 output;(c)filter 條件太嚴格——沒有任何 item 符合,結果空陣列,正常但要檢查條件是不是打太緊。 -
Code 執行很慢(>30 秒 timeout)
通常是大陣列(>10,000 筆)在跑
.map/.filter。解法:上游先接 Split In Batches 節點 分批處理,每批 500-1000 筆送進 Code node。或者檢查 code 有沒有巢狀 loop(.map內又.filter)——那是 O(n²),資料一多就爆炸。 -
Items to return were not valid你 return 的東西不是 n8n item 格式。正確格式:
[{ json: {...} }]——外層陣列、每個元素是有jsonkey 的物件。錯誤:return { name: 'Elmo' }(不是陣列)、return [{ name: 'Elmo' }](沒包json)。Each Item 模式回 一個{ json: {} };All Items 模式回陣列。 -
ReferenceError: fetch is not defined或require is not definedCommunity 版 sandbox 沒開
fetch跟require。呼叫 API 改用await this.helpers.httpRequest({...});想引 npm 套件的話,self-host 才有解——設環境變數NODE_FUNCTION_ALLOW_EXTERNAL=lodash才會開放(Cloud 版無法)。
常見問題
我完全不會 JavaScript,一定要學嗎?
Python 好還是 JavaScript 好?
_input.item['json']['x']),從 JS 範例改寫要注意。什麼時候用 Python:你有現成 Python 邏輯(例:某個字串處理 function、公司內部工具的 Python 片段)要直接搬過來、又能 pip install 需要的套件。純粹「我比較會 Python」不是好理由。Code node 可以用 npm 套件(例:lodash、axios)嗎?
crypto、querystring 這種)。Self-host 可以,但要在 n8n container 的環境變數設 NODE_FUNCTION_ALLOW_EXTERNAL=lodash,axios(用逗號列你要開的套件名),還要 npm install 進 container。Cloud 用戶想要類似效果,把邏輯拆成 sub-workflow(第 19 章)或改用 HTTP Request 呼叫外部服務。兩個 Code node 之間可以共用變數嗎?
const x = 5 在下一個 Code node 看不到。要在節點之間傳資料,就走 n8n 的正規做法:把資料塞進 output 的 json,下一個節點用 $('前一個 Code').item.json.x 抓。想全 workflow 共用常數的話用 Set 節點存或 workflow variables(Enterprise 才有 $vars)。Code node 可以 await 呼叫其他 workflow 嗎?
await 的東西是 this.helpers.httpRequest(呼叫外部 API)跟其他回 Promise 的內建 helper。想在 Code node 裡跑 sub-workflow 的話,改成:Code node 準備好資料 → Execute Workflow 節點 → 下一個 Code node 接續處理。這樣分工也比較清楚。ChatGPT 生的 Code node code 能不能直接貼?
items[0].json(舊 v0 寫法)而不是 $input.all()[0].json(新寫法),跑起來會 items is not defined;(b)output 有沒有包 { json: {} } 格式。給 AI 生 code 時明確告訴它「n8n Code node、JavaScript、Run Once for All Items 模式、用 $input.all()」,會生對的機率高很多。生完先小資料量試跑一次再上線。Code node 跟 Expression 差在哪?
await。Code node 是完整節點,可以寫很多行、能宣告變數、能跑 loop、能 await、能 console.log。判斷公式:邏輯一行 {{ }} 塞得下就用 Expression;一行塞不下(要 if/else 多分支、要 for loop、要暫存中間值)就用 Code node。Expression 適合「動態填值」,Code node 適合「處理邏輯」。怎麼知道我寫的 code 在 Cloud 版能不能跑?
fetch is not defined、require is not defined、Module not found)。原則上:(a)內建 JS 語法全部 OK(陣列方法、字串方法、JSON、Math、Date);(b)n8n helper($input、$now、this.helpers.httpRequest)OK;(c)npm 套件、fs 檔案系統、直接 fetch 通常不行。有疑慮先做 sandbox test workflow 跑幾行試水溫。