关于 n8n-code-nodes-official
为使用 n8n 的 Agent 提供 Code 节点选择与编写指导,适用于需要自定义数据处理的工作流。它优先推荐表达式和 Edit Fields,并说明跨数据源聚合等适合使用 Code 节点的场景。
主要功能
- 01按决策顺序选择表达式或 Code
- 02使用 Edit Fields 箭头函数处理数据
- 03为多数据源聚合使用 Code 节点
- 04了解 JavaScript Code 节点的运行模式
- 05优先使用原生 Crypto 和 XML 节点
适用场景
- 01转换少量字段而避免额外 Code 节点
- 02汇总多个上游数据源
- 03解析 XML 后提取字段
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ n8n-io/ skills --skill n8n-code-nodes-official安装后在 Claude Code 中输入/n8n-code-nodes-official确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/ARROW_FUNCTIONS_IN_EDIT_FIELDS.mdDECISION_TREE.mdJAVASCRIPT_PATTERNS.md详细介绍
n8n Code 节点
Code 节点功能强大,但往往不是正确的工具。它相当于在 ORM 足以解决问题时直接使用原始 SQL:确实存在适合它的实际场景,但只要 Code 节点处理了表达式也能完成的逻辑,工作流就会变得更难阅读、调试和维护。性能差距也确实存在:Code 在沙盒 JS 运行时中运行,而表达式和 Edit Fields 则在进程内运行;对于等效逻辑,Code 的每次调用开销可能高出数百倍(据说约为 2ms 对 600ms)。对于热点路径和大量项目,这种差距会不断累积。
强默认选择
-
Code 节点是最后的选择。 决策顺序:表达式(
{{...}})→ Edit Fields 中的箭头函数 → Code 节点。前两种方式可以处理大多数“转换这些数据”任务。多源聚合、外部库,以及下文记录的少数特定模式,才值得使用 Code。 -
默认使用 JavaScript。 除非用户明确要求使用 Python(“这里用 Python”“我们团队使用 Python”,或粘贴了 Python 代码),否则就写 JS。n8n 的其他所有地方(表达式、Edit Fields)都使用 JS,而且 JS 有经过筛选的库允许列表(
lodash、crypto、luxon)。
决策树
Need custom logic?
├── Is it a transformation of one or two fields?
│ └── Expression: {{ $json.foo.toUpperCase() }} or {{ $json.items.map(item => item.name).join(', ') }}
│ Most "just transform this" cases land here.
│
├── Is it multi-line, but pure data shaping (map, filter, reduce, conditional)?
│ └── Edit Fields with arrow function expression. See references/ARROW_FUNCTIONS_IN_EDIT_FIELDS.md
│
├── Does it need full statements, multiple data sources, or external libs?
│ ├── Are you SURE the above two don't work?
│ │ └── Re-read the parent. The bar is high.
│ └── Yes, genuinely needs it
│ └── Code node. See references/JAVASCRIPT_PATTERNS.md
│
└── Is it actually two separate transformations stitched together?
└── Use two nodes (Edit Fields → Edit Fields, or Edit Fields → IF). Composability beats one big Code block.
有关完整决策逻辑及每个分支的示例,请参阅 references/DECISION_TREE.md。
人们容易忽略表达式能做什么
常见的、让人想用 Code 节点的场景,其实应该用表达式:
// ❌ Code node
return { name: $input.first().json.name.toUpperCase() }
// ✅ Expression in Edit Fields, "name" field
{{ $json.name.toUpperCase() }}
// ❌ Code node
const items = $input.first().json.items
return { tags: items.map(item => item.tag).filter(tag => tag).join(', ') }
// ✅ Expression
{{ $json.items.map(item => item.tag).filter(tag => tag).join(', ') }}
// ❌ Code node
const date = new Date($input.first().json.created_at)
return { formatted: date.toISOString().slice(0, 10) }
// ✅ Expression with n8n's date extension
{{ $json.created_at.toDateTime().format('yyyy-MM-dd') }}
有关表达式能实现什么的更多信息,请参阅 n8n-expressions-official 技能。
Edit Fields 中的箭头函数能做什么
Edit Fields 通过表达式赋值。内联箭头函数可以让你在不使用 Code 节点的情况下,完成大多数多行逻辑:
// In Edit Fields, "summary" field:
{{ (() => {
const items = $json.items
const total = items.reduce((sum, item) => sum + item.price, 0)
const tax = total * 0.08
return `Total: $${(total + tax).toFixed(2)}`
})() }}
当“逻辑稍微复杂,无法写成单行”时,就该使用它。有关模式和格式,请参阅 references/ARROW_FUNCTIONS_IN_EDIT_FIELDS.md。
适合使用 Code 节点的场景
确实存在适用场景。门槛高,但不是“绝对不能用”。以下情况都合理。放心使用代码,不必抱歉。
跨整个数据集进行多源聚合
当一个节点需要:
- 同时从多个上游节点读取数据(例如,
$('Source A').all()、$('Source B').all()、$('Source C').first().json)。 - 一次性针对所有项目(而非逐项)计算统计数据或进行转换。
- 使用中间数据结构(查找映射、累加器、比率)执行多步逻辑。
这是最常见的合理场景。示例:
- 分析汇总:按组计算平均值、百分位评分、跨类别归一化。
- 从一个来源构建查找表,再与另一个来源的行进行连接。
- 根据两个参考数据集计算偏移量或比率,再应用于第三个数据集。
// Real-world shape: aggregate test results from one source,
// model metadata from another, category mapping from a third,
// and produce per-model-per-category averages.
const testResults = $('Get Test Results').all().map(item => item.json)
const models = $('Get Models').all().map(item => item.json)
const categoryMap = $('Get Category Map').first().json.testCategoryMap
const categoryByTestId = Object.fromEntries(
categoryMap.map(mapping => [mapping.testId, mapping.category])
)
const result = models.map(model => {
const modelTests = testResults.filter(test => test.modelId === model.id)
const stats = modelTests.reduce((acc, test) => {
const cat = categoryByTestId[test.testId]
if (!cat) return acc
acc[cat] ??= { scored: 0, available: 0, count: 0 }
acc[cat].scored += test.pointsScored ?? 0
acc[cat].available += test.pointsAvailable ?? 0
acc[cat].count += 1
return acc
}, {})
const averages = Object.fromEntries(
Object.entries(stats).map(([category, stat]) => [
category,
{ avg: stat.available > 0 ? stat.scored / stat.available : 0, n: stat.count }
])
)
return { modelId: model.id, modelName: model.modelName, ...averages }
})
return result.map(json => ({ json }))
从技术上说,表达式可以通过 $('Node Name').all() 跨项目读取数据,并内联执行归约;但对于结构如此复杂的情况(连接、分组汇总、嵌套聚合),最终会变成难以阅读且无法调试的单行巨型表达式。请使用 Code。
外部库
JS Code 可以通过 require 使用经过筛选的允许列表中的库(如 lodash),但表达式不行。
务必先检查是否有原生节点。 n8n 的原生节点比很多人想象的更多。对于原生节点已经能处理的事情仍使用 Code,是一个反复出现的错误。接下来的两个小节会介绍具体容易踩的坑。
加密操作:使用 Crypto 节点,而非 Code
HMAC、签名、哈希、加密:n8n 有原生 Crypto 节点(n8n-nodes-base.crypto)。 请使用它。它可以处理 SHA256、MD5、HMAC、加密/解密和随机数生成,无需编写 JavaScript。
// WRONG (recurring AI slip):
const crypto = require('crypto')
const hash = crypto.createHash('sha256').update(buf).digest('hex').slice(0, 12)
// RIGHT: configure the Crypto node with operation: 'hash', type: 'SHA256'
// then read $('Hash').item.json.<output> downstream.
使用 require('crypto') 的 Code 模式是最常见的“这需要 Code 节点”误判之一。其实不需要。Crypto 节点就能处理。
对二进制数据进行哈希,而不是对字符串进行哈希
不要仅仅因为需要对二进制数据(PDF、图像、文件缓冲区)进行哈希就使用 Code。Crypto 节点有一个 binaryPropertyName 参数。将它指向二进制属性键,它就会直接对缓冲区进行哈希。你不需要在用户代码中调用 this.helpers.getBinaryDataBuffer(...)。
// WRONG (recurring AI slip with binary):
const crypto = require('crypto')
const buf = await this.helpers.getBinaryDataBuffer($itemIndex, 'data')
const hash = crypto.createHash('sha256').update(buf).digest('hex')
// RIGHT: Crypto node configured to hash binaryPropertyName='data', SHA256.
// Then $('Crypto').item.json.<output> has the hash; chain a Set node for any field shaping.
剩下唯一合理的加密场景:需要使用 Crypto 节点未提供的非标准签名方案(例如自定义的 AWS 风格签名),并且 httpCustomAuth 凭据也不适用。这种情况很少见。请明确说明理由。
XML / SOAP / RSS 解析:使用 XML 节点,而不是 Code
n8n 有一个原生 XML 节点(n8n-nodes-base.xml),支持解析和字符串化操作。它已经可以将 XML 转换为 JSON。转换后,结果就是普通 JSON,使用带箭头函数表达式的 Edit Fields 即可完成字段提取、数组规范化(Array.isArray(...) ? ... : ...)以及查找链接(.find()),这些都是你可能会用 Code 来做的操作。
// WRONG (recurring AI slip):
// XML node already parsed → another Code node to extract a few fields:
const entry = $('Parse XML').item.json.feed.entry
const firstEntry = Array.isArray(entry) ? entry[0] : entry
return { json: { title: firstEntry.title, url: firstEntry.link.find(link => link.type === 'pdf').href } }
// RIGHT: Edit Fields with arrow function expressions:
// title: ={{ (() => { const entry = $('Parse XML').item.json.feed.entry; return Array.isArray(entry) ? entry[0].title : entry.title; })() }}
// pdfUrl: ={{ $('Parse XML').item.json.feed.entry.link.find(link => link.type === 'pdf')?.href }}
如果字段提取逻辑确实太复杂,即使用多行箭头函数也无法用内联表达式处理,接下来应使用带有单个多行箭头函数的 Edit Fields,而不是 Code 节点。参见 references/ARROW_FUNCTIONS_IN_EDIT_FIELDS.md。
这些情况的共同点
合理的场景与作用范围有关:整个数据集、多个来源,或单条目工具无法处理的有状态结构。不合理的场景包括:Code 做了表达式就能完成的工作,或者原生节点已经能完成的工作。
快速判断:
- “我能否把这个 Code 节点的工作描述为‘接收这一个条目并……’?” 如果可以,那就用错工具了。
- “有没有原生节点能做这件事?” 先通过
search_nodes搜索。Crypto、XML、JSON 解析、日期计算(Luxon)、HTTP 调用、文件 I/O、正则匹配:都有原生节点或表达式级支持。
JavaScript Code 节点详解
两种模式:
- Run Once for All Items(默认,使用此模式): 使用
$input.all()运行一次。如果需要逐条处理,只需在内部使用for (const item of $input.all())。这是标准写法,几乎总是你需要的模式。 - Run Once for Each Item: 每个条目运行一次,使用
$input.first()(或$input.item)。
常见写法:
// Run Once for All Items
const items = $input.all()
const totals = items.map(item => ({
...item.json,
total: item.json.qty * item.json.price,
}))
return totals.map(json => ({ json }))
返回值必须是 { json: ... } 对象组成的数组(或 { json: ..., binary: ... }),不能是原始 JSON。
有关二进制数据处理、错误模式和仅限 Code 节点的漏洞,请参见 references/JAVASCRIPT_PATTERNS.md。
参考文件
| 文件 | 阅读时机 |
|---|---|
references/DECISION_TREE.md | 想要使用 Code 节点,并希望确认更简单的方法确实行不通时 |
references/ARROW_FUNCTIONS_IN_EDIT_FIELDS.md | 转换需要多行代码,但只是纯数据整形时 |
references/JAVASCRIPT_PATTERNS.md | 确实需要 Code 节点,且使用 JavaScript 时 |
反模式
| 反模式 | 问题所在 | 修复方法 |
|---|---|---|
Code 节点执行 return { x: $input.first().json.x.toUpperCase() } | 仅为一个表达式就使用整个节点 | 替换为 Edit Fields 表达式 |
| Code 节点为电子邮件正文构建 HTML 字符串 | Email 节点的正文栏支持表达式 | 将表达式内联到 Email 节点中 |
Code 节点使用 new Date() 格式化日期 | 不如 Luxon 清晰 | 在表达式中使用 Luxon。参见 n8n-expressions-official |
| Set 节点 + Code 节点组合(Set 构建输入,Code 进行转换) | 两个节点做了本应由一个 Edit Fields 完成的事 | 合并为一个带箭头函数的 Edit Fields |
| 将凭据/令牌粘贴到 Code 节点文本中 | 与文本字段一样会导致泄露 | 使用凭据,不要使用 Code 节点 |