关于 n8n-code-javascript
面向 n8n Code 节点 JavaScript 编写与排错的技能指南,涵盖执行模式、数据访问和返回格式,并强调优先考虑原生节点与表达式。
主要功能
- 01选择全量或逐项执行模式
- 02访问输入项及其他节点数据
- 03遵循 Code 节点返回格式
- 04参考常见处理模式与错误说明
- 05优先评估原生节点和表达式
适用场景
- 01编写需要跨输入项处理的代码
- 02转换或校验单条输入数据
- 03排查 Webhook 数据访问或返回错误
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ czlonkowski/ n8n-skills --skill n8n-code-javascript安装后在 Claude Code 中输入/n8n-code-javascript确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
BUILTIN_FUNCTIONS.mdCOMMON_PATTERNS.mdDATA_ACCESS.mdERROR_PATTERNS.mdREADME.md详细介绍
JavaScript 代码节点
在 n8n 代码节点中编写 JavaScript 的专家指南。
编写代码节点之前
新建代码节点必须先通过以下关卡:
- 是否有适用于此任务的原生节点? 查看
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(分页、批处理)和 Crypto 可满足大多数需要编写代码的场景。 它们的配置见n8n-node-configuration→ NATIVE_NODES.md。 - 该节点能否通过简短表达式完成任务? 一个表达式,不含函数体——辅助方法(
.pluck()、.sum()、.unique()、$ifEmpty()、.diffTo())见n8n-expression-syntax,并且在 n8n-mcp 2.97.0+ 中也可通过tools_documentation({topic: "expression_helpers"})查看。在{{ }}中写多条语句就算代码,因此应放在这里,而不是 Edit Fields 字段中。 - 该任务是否属于允许列表? 没有节点能实现的循环或递归、跨运行状态(
$getWorkflowStaticData)、没有节点能处理的解析任务、 允许列表中的库、繁重的全项目数据处理(数千条数据,明示或实测),或者用一个简短代码节点(约 15 行)替代 4 个以上仅用于重整数据的连接节点。
如果通过关卡,请编写代码节点并添加说明,让后续阅读者(以及审查钩子)了解原因:
{ "name": "Flatten category tree", "type": "n8n-nodes-base.code", "notes": "Why Code: Split Out considered — the tree has arbitrary depth (recursion)", "notesInFlow": true }
编辑或调试现有代码节点无需通过关卡;但如果该节点执行的是原生节点的工作,请指出这一点并提供原生节点替代方案。
快速入门
// Basic template for Code nodes
const items = $input.all();
// Process data
const processed = items.map(item => ({
json: {
...item.json,
processed: true,
timestamp: new Date().toISOString()
}
}));
return processed;
基本规则
- 选择“Run Once for All Items”模式(大多数场景推荐)
- 访问数据:
$input.all()、$input.first()或$input.item - 返回
[{json: {...}}]——这是规范且可跨模式使用的形式。在 Run Once for All Items 模式下,n8n 也会自动将裸return {…}对象包装起来,因此这种写法也能运行;真正会失败的是返回基本类型(字符串/数字)或null。 - 重要:Webhook 数据位于
$json.body下(而不是直接位于$json) - 可用的内置功能:
this.helpers.httpRequest()(不支持身份验证——在任务运行器沙箱中,裸$helpers全局变量为 undefined,因此$helpers.httpRequest()会抛出ReferenceError: $helpers is not defined)、DateTime(Luxon)、$jmespath()。不可用:this.helpers.httpRequestWithAuthentication(列入拒绝列表)、$env(当 N8N_BLOCK_ENV_ACCESS_IN_NODE=true 时)、require()(除非列入允许列表)。对于超出简单无身份验证 GET 请求的需求(身份验证、分页、重试),优先使用 HTTP Request 节点,并让代码节点专注于纯逻辑。 - 实例允许使用的库:可自托管实例能通过
N8N_RUNNERS_ALLOWED_BUILT_IN_MODULES和N8N_RUNNERS_ALLOWED_EXTERNAL_MODULES(旧版:NODE_FUNCTION_ALLOW_BUILTIN/NODE_FUNCTION_ALLOW_EXTERNAL)将模块列入允许列表。如果用户表示其实例允许使用特定模块(例如axios、lodash、crypto),请通过require()使用,不要拒绝。如果不确定,请询问,或默认只使用内置模块。 - 技能不匹配? 如果你正在为连接到 AI Agent 的 Custom Code Tool(
@n8n/n8n-nodes-langchain.toolCode)编写代码,请停下——该节点使用不同的约定(通过query输入,必须返回字符串,不使用$input/$helpers)。请使用 n8n-code-tool 技能。
模式选择指南
代码节点提供两种执行模式。请根据使用场景进行选择:
对所有项运行一次(推荐 - 默认)
适用场景: 95% 的使用场景
- 工作方式:无论输入数量多少,代码都只执行一次
- 数据访问:
$input.all()或items数组 - 最适合:需要一次处理所有项,且已通过上述判断的逻辑
- 性能:处理多项时更快(仅执行一次)
// Example: running balance — each item depends on the ones before it,
// which no native node expresses (Summarize gives one total per group, not a running one)
let balance = 0;
return $input.all().map(item => {
balance += item.json.amount || 0;
return { json: { ...item.json, balance } };
});
适用情况:
- ✅ 每一项都依赖前面的项(运行总计、会话划分、间隔检测)
- ✅ 对未知深度的嵌套数据运行多轮算法或递归
- ✅ 对数千项进行复杂转换,且实测一个节点优于长链节点
优先使用原生节点:总计、平均值和计数 → Summarize;排序或排名 → Sort + Limit;去重 → Remove Duplicates;将各项合并到一个数组 → Aggregate;合并两个列表 → Merge。
对每一项分别运行
适用场景: 仅限特殊情况
- 工作方式:针对每个输入项单独执行代码
- 数据访问:
$input.item或$item - 最适合:针对单项的逻辑、独立操作、逐项验证
- 性能:处理大型数据集时较慢(需要多次执行)
// Example: Add processing timestamp to each item
const item = $input.item;
return [{
json: {
...item.json,
processed: true,
processedAt: new Date().toISOString()
}
}];
适用情况:
- ✅ 每一项都需要独立的 API 调用
- ✅ 逐项验证,且错误处理方式各不相同
- ✅ 根据项的属性进行针对该项的转换
- ✅ 业务逻辑要求分别处理各项
快速判断:
- 需要查看多项? → 使用“所有项”模式
- 每一项都完全独立? → 使用“每一项”模式
- 不确定? → 使用“所有项”模式(也可以随时在内部循环)
为什么“所有项”模式更快——逐项执行边界
在 Code 节点中,模式选择是影响性能的最大因素。每次逐项执行的上下文都会产生启动开销(基于 n8n 2.x 的小型记录测量):
| 每项运行的内容 | 大致开销 |
|---|---|
| Code 所有项(对整个集合运行一次) | ~0.02 ms/项 |
| 任意节点中的表达式(IF / Set / 等) | ~0.2 ms/项 |
| Code 每一项(每项都启动完整的沙箱) | ~0.6 ms/项 — 比“所有项”模式慢约 25–30 倍 |
因此,对 1 万项使用 Run Once for Each Item,纯开销约为 6 秒;而使用 Run Once for All Items 约为 0.2 秒。只有在确实需要隔离单项时,才使用“每一项”模式(例如独立的错误处理,或无法批处理的逐项 API 调用);否则,应在一个“所有项”节点内部循环。表达式的复杂度基本不会产生额外开销(约 90% 的成本来自逐项上下文,而非代码本身),而且每次节点间传递都会重新复制所有项——因此应减少逐项执行边界的数量,而不是对每个边界进行微优化。
除逐项开销外,每次运行 Code 节点还要向任务运行器进行一次固定的交接。在默认的内部模式下,运行器会在空闲 15 秒后停止(N8N_RUNNERS_AUTO_SHUTDOWN_TIMEOUT),因此偶尔运行的工作流在大多数运行时都会产生数百毫秒的冷启动开销。原生转换节点会在进程内通过一次调用处理所有项,从而跳过这次交接。
这对节点选择意味着什么。 大多数工作流都受 I/O 限制,且处理几百项以下时,这些开销都无关紧要:应根据可读性进行选择,也就是优先使用原生节点。只有在处理大量项的复杂“所有项”任务中——每次运行数千项,转换工作而非 I/O 占主导,并且这一需求由用户明确提出或经测量确认——性能才是决定性因素。此时,一个“所有项”Code 节点可能优于一长串节点,并应在其 Why Code: 说明中解释原因。这些数据是本套件唯一经过测量的参考数据;其他技能会链接到此处,而不重复这些数据。
另请参阅:DATA_ACCESS.md → “模式性能”,了解相关推论、节点间传递开销和规模检查。
数据访问模式
从上游节点提取数据的四种方式。请注意,$node["Name"] 和 $('Name') 需要使用 .first().json 或 .all(),绝不能直接使用 .json。
const allItems = $input.all(); // 1. All items — batch ops, aggregation (most common)
const data = $input.first().json; // 2. First item — single objects, API responses
const item = $input.item; // 3. Current item — "Each Item" mode ONLY (undefined otherwise)
const other = $node["Webhook"].json; // 4. Named node — combine data across nodes
始终通过 .json 访问字段(例如,item.json.name,而不是 item.name),并优先使用明确的 $input.first().json.field,而不是简写的 $json.field。
参见:DATA_ACCESS.md,了解完整指南——其中包含每种模式的示例、决策树,以及常见错误(修改原始数据、缺少长度检查、在错误模式下使用 $input.item)。
关键:Webhook 数据结构
最常见的错误:Webhook 数据嵌套在 .body 下
// ❌ WRONG - Will return undefined
const name = $json.name;
const email = $json.email;
// ✅ CORRECT - Webhook data is under .body
const name = $json.body.name;
const email = $json.body.email;
// Or with $input
const webhookData = $input.first().json.body;
const name = webhookData.name;
原因:Webhook 节点会将所有请求数据封装在 body 属性下,其中包括 POST 数据、查询参数和 JSON 负载。
参见:DATA_ACCESS.md,了解完整的 Webhook 结构详情
返回格式要求
规范格式:[{json: {...}}]——一个对象数组,每个对象都有一个 json 属性。该格式明确无歧义,在两种执行模式下的表现完全一致,因此应将其作为默认格式。
在 Run Once for All Items 模式下,n8n 会在输出时自动规范化较宽松的格式:单个裸对象或裸对象数组都会自动封装在 json 下。因此,return {foo: 1} 可以运行。无法封装、因此会在运行时真正失败并报错 "Code doesn't return items properly" 的,是原始值(字符串、数字或布尔值)或 null/undefined。(n8n-mcp ≥ 2.63.0 不再将裸对象返回标记为错误;这反映了自动封装行为。)
正确的返回格式
// ✅ Single result
return [{
json: {
field1: value1,
field2: value2
}
}];
// ✅ Multiple results
return [
{json: {id: 1, data: 'first'}},
{json: {id: 2, data: 'second'}}
];
// ✅ Transformed array
const transformed = $input.all()
.filter(item => item.json.valid)
.map(item => ({
json: {
id: item.json.id,
processed: true
}
}));
return transformed;
// ✅ Empty result (when no data to return)
return [];
// ✅ Conditional return
if (shouldProcess) {
return [{json: processedData}];
} else {
return [];
}
非规范返回格式(会自动封装——优先使用规范格式)
// ⚠️ Auto-wrapped in All Items mode → [{json: {field: value}}]. Runs, but prefer the array form.
return {
json: {field: value}
};
// ⚠️ Auto-wrapped → [{json: {field: value}}]. Runs, but add the json wrapper for clarity.
return [{field: value}];
// ✅ Fine — input items already carry a json property, so returning them unchanged is a valid passthrough
return $input.all();
确实有问题的返回格式
// ❌ FAILS: primitive — n8n errors "Code doesn't return items properly"
return "processed";
// ❌ FAILS: null / undefined — nothing to pass to the next node
return null;
重要性:规范格式 [{json: {...}}] 明确无歧义,且在两种模式下表现相同。n8n 会在 All Items 模式下自动规范化裸对象和对象数组,但原始值或 null 返回值无法封装,会导致执行停止。
参见:ERROR_PATTERNS.md #3,了解详细的错误解决方法
常见模式概览
这些是生产工作流中最实用的 Code 节点结构,适用于 Code 通过准入检查的情况。其中一些模式有原生替代方案(总计 → Summarize,取前 N 项 → Sort + Limit,JSON 比较 → Compare Datasets,字符串聚合 → Aggregate 或 Summarize concatenate);库中的每种模式都以其原生替代方案开篇。完整库涵盖 10 种模式:多源聚合、正则表达式筛选、Markdown/结构化文本解析、JSON 比较、CRM/表单转换、版本发布处理、带计算字段的数组转换、Slack Block Kit 格式化、前 N 项排名,以及字符串聚合报告——每种模式都包含变体。
参见:COMMON_PATTERNS.md,了解 10 种详细的生产模式(以及最佳实践部分:验证输入、使用 try-catch、尽早筛选、优先使用数组方法而非循环、使用 console.log 调试)。
错误预防——常见错误
Code 节点反复出现的故障,按大致发生频率排序:
- 代码为空/缺少返回值——始终以
return [...]结束,并确保每个分支都有返回值。 - 把表达式语法当作代码——JavaScript 代码中不要写
{{ }}(return {{ $json.x }}会导致语法错误)。请使用`${$json.field}`或$input.first().json.field。在字符串字面量内部使用{{ }}没问题——它只是 n8n 不会求值的字面文本。 - 返回值形状——优先使用
return [{json:{...}}]。在 All Items 模式下,裸写return {…}会自动包装,但返回基本类型(字符串/数字)或null才会真正导致失败。 - 缺少 null 检查——使用可选链:
item.json?.user?.email || 'fallback'。 - Webhook 请求体嵌套——
$json.email未定义;请使用$json.body.email。 - 身份验证辅助函数被禁用(
httpRequestWithAuthentication),且$env被禁用——请通过凭据/HTTP Request 节点处理密钥,而不是在 Code 节点沙箱中处理。
参见:ERROR_PATTERNS.md,其中提供了完整指南——每种错误的错误/正确代码、转义规则、沙箱限制(错误 #6–#7)、预防检查清单,以及快速错误消息查找表。
内置函数与辅助工具
// HTTP requests (no auth — see sandbox note below)
const res = await this.helpers.httpRequest({ method: 'GET', url: 'https://api.example.com/data' });
// DateTime (Luxon): now, formatting, arithmetic
const now = DateTime.now();
const formatted = now.toFormat('yyyy-MM-dd');
const tomorrow = now.plus({ days: 1 });
// $jmespath() — query JSON structures
const adults = $jmespath($input.first().json, 'users[?age >= `18`]');
// $getWorkflowStaticData() — data that persists across executions
**沙箱(自 n8n v2.0 起,JsTaskRunnerSandbox):**访问器是 this.helpers.httpRequest()——这里的 $helpers 全局变量未定义($helpers.httpRequest() 会抛出 ReferenceError)。在丢失 this 的嵌套异步函数中,请使用 await fn.call(this, ...) 调用。this.helpers.httpRequestWithAuthentication 和 this.helpers.requestWithAuthenticationPaginated 被列入拒绝列表(→ UnsupportedFunctionError);进行身份验证调用时,请使用携带凭据的 HTTP Request 节点(首选)、子工作流,或仅当令牌已作为数据流经工作流时,在 this.helpers.httpRequest() 上手动设置 Authorization: Bearer ${token} 标头。当 N8N_BLOCK_ENV_ACCESS_IN_NODE=true 时,$env 会被禁用;require() 仅适用于允许列表中的模块。Buffer、URL 和标准 JS 全局对象(Math、JSON、Object、Array)始终可用。
参见:BUILTIN_FUNCTIONS.md,其中提供了完整参考——完整的 httpRequest 选项、所有 DateTime/Luxon 操作、JMESPath 模式、静态数据用例,以及沙箱限制的详细说明。
最佳实践
- 先验证输入——处理前检查数组是否为空,以及
.json是否缺失。 - 对有风险的操作使用 try-catch(HTTP 调用),并返回错误对象,而不是让程序崩溃。
- 优先使用数组方法(
filter/map/reduce),而不是手动循环。 - 尽早筛选,稍后转换——在执行开销较大的操作前缩减数据集。
- 使用描述性名称,并通过
console.log()调试(输出会显示在浏览器控制台中)。
参见:COMMON_PATTERNS.md → “最佳实践”,其中提供了每项实践的代码示例。
生产环境中的注意事项
从真实部署中总结出的经验教训,汇总于此,代码示例见 DATA_ACCESS.md → “生产环境中的注意事项”:
- SplitInBatches 的输出有悖直觉:
main[0]= 完成(所有批次处理完后触发一次),main[1]= 每个批次(循环体)。在完成输出后添加一个 Limit 1 节点作为保险。 - 迭代次数决定成本:每次循环迭代都会通过引擎重新运行整个循环体(每次约有 0.8 ms 的开销)。
batchSize: 1相当于 Each Item 的循环用法——使用真实约束(速率限制、页面大小、内存)允许的最大批次,或者完全不要循环。 - 跨迭代累积(重要):循环结束后,
$('Node Inside Loop').all()只会返回最后一次迭代的项目。通过$getWorkflowStaticData('global')累积(循环前重置,循环内推入,循环后读取)。 - pairedItem:输出与输入不是一对一对应的项目时,设置
pairedItem: { item: i },否则下游 Set 节点会因paired_item_no_info失败。 - 节点引用语法:使用
$('Node').first().json或$('Node').all()——绝不要直接在引用上使用.json。 - 浮点精度:在厘的精度上比较货币——
Math.round(a*100) !== Math.round(b*100)——避免浮点误差导致误报。
何时使用 Code 节点
Code 是最后的选择,应在服务自身的节点、用于该任务的原生节点,以及该节点中的简短表达式之后再考虑。 本技能顶部的判断标准就是测试;以下是它检查的封闭清单:
| 适合使用 Code 的情况 | 示例 | 不用 Code |
|---|---|---|
| 没有节点能实现的循环或递归 | 展开未知深度的树;多轮匹配 | 单层嵌套 → Split Out |
| 跨运行状态 | $getWorkflowStaticData 中的计数器或游标 | “仅处理新项目” → 在多次执行间使用 Remove Duplicates |
| 没有节点能处理的解析任务 | 定长记录、自定义日志格式 | CSV / XLSX / PDF / XML / HTML → Extract From File、XML、HTML |
| 允许使用的库 | 实例允许的 require('some-lib') | 哈希 / HMAC / UUID → Crypto |
| 大量项目的繁重处理 | 数千个项目,受转换限制,且有明确说明或测量依据 | 几百个项目 → 原生节点 |
| 用一个简短节点替代 4 个或更多胶水节点 | 约 15 行代码,可替代只重塑数据的 Set → Split Out → Set → Aggregate 链 | 含有实际路由或 I/O 操作的节点链 |
不在清单中的任务不应使用 Code:筛选(Filter)、路由(IF / Switch)、总计和计数(Summarize)、前 N 项(Sort + Limit)、 去重(Remove Duplicates)、联接(Merge)、差异比较(Compare Datasets)、日期处理(Date & Time 或 Luxon 表达式)、API 调用 和分页(HTTP Request)、加密和 XML(Crypto、XML)。
每个保留的 Code 节点都必须带有备注 Why Code: <native node considered> — <reason>(设置 notes,并设置
notesInFlow: true 以在画布上显示)。如果 Code 节点没有此类备注,创建 / 更新钩子会提醒你。
与其他技能集成
适用场景:
n8n 表达式语法:
- 其他节点中的表达式使用
{{ }}语法 - Code 节点直接使用 JavaScript(不使用
{{ }}) - 了解何时使用表达式与代码
n8n MCP 工具专家:
- 查找 Code 节点:
search_nodes({query: "code"}) - 获取配置帮助:
get_node({nodeType: "nodes-base.code"}) - 验证代码:
validate_node({nodeType: "nodes-base.code", config: {...}})
n8n 节点配置:
- 模式选择(All Items 与 Each Item)
- 语言选择(JavaScript 与 Python)
- 理解属性依赖关系
n8n 工作流模式:
- 在转换步骤中使用 Code 节点
- Webhook → Code → API 模式
- 工作流中的错误处理
n8n 验证专家:
- 验证 Code 节点配置
- 处理验证错误
- 自动修复常见问题
快速参考清单
部署 Code 节点前,请确认:
- 通过门槛 - 没有适用的原生节点;
notes以Why Code:开头 - 代码不为空 - 必须包含有意义的逻辑
- 存在返回语句 - 返回条目,而不是基本类型或
null - 规范的返回格式 - 每个条目:
{json: {...}}(裸对象会自动包装,但请明确指定) - 数据访问正确 - 使用
$input.all()、$input.first()或$input.item - 不要将
{{ }}写成代码 - 使用 JavaScript 模板字面量:`${value}` - 错误处理 - 使用守卫子句检查
null/undefined输入 - Webhook 数据 - 如果数据来自 webhook,请通过
.body访问 - 模式选择 - 大多数情况下选择“All Items”
- 性能 - 优先使用 map/filter,而非手动循环
- 输出一致 - 所有代码路径都返回相同结构
其他资源
相关文件
- DATA_ACCESS.md - 全面的数据访问模式
- COMMON_PATTERNS.md - 10 个经过生产环境验证的模式
- ERROR_PATTERNS.md - 5 大错误及解决方案
- BUILTIN_FUNCTIONS.md - 完整的内置功能参考
n8n 文档
- Code 节点指南:https://docs.n8n.io/code/code-node/
- 内置方法:https://docs.n8n.io/code-examples/methods-variables-reference/
- Luxon 文档:https://moment.github.io/luxon/
准备好在 n8n Code 节点中编写 JavaScript 了! 从简单的转换开始,使用错误模式指南避免常见错误,并参考模式库中的生产就绪示例。