mcpField
N
n8n-code-nodes

n8n-code-nodes

github.com/n8n-io开发558348 次安装2 周前更新
代码仓库

关于 n8n-code-nodes

为 n8n Code 节点提供选择与编写指南,帮助工作流构建者优先使用表达式或 Edit Fields 处理简单转换,并将 JavaScript 留给跨数据集或需用库的场景。

主要功能

  1. 01表达式、Edit Fields 与 Code 的选择指南
  2. 02跨数据集聚合示例
  3. 03JavaScript Code 节点编写模式
  4. 04以原生节点处理常见任务的建议

适用场景

  1. 01为数据转换选择合适节点
  2. 02合并并聚合多个来源的数据
  3. 03判断原生节点能否替代自定义代码

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/n8n-io/skills --skill n8n-code-nodes

安装后在 Claude Code 中输入/n8n-code-nodes确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
ARROW_FUNCTIONS_IN_EDIT_FIELDS.md
DECISION_TREE.md
JAVASCRIPT_PATTERNS.md

详细介绍

n8n Code 节点

Code 节点功能强大,但通常不是合适的工具。它相当于 n8n 中的“当 ORM 就能解决问题时,却直接使用原始 SQL”:确实有适用场景,但只要 Code 节点处理的逻辑原本可以用表达式实现,工作流就会更难阅读、调试和维护。性能差距也确实存在:Code 在沙箱化的 JS 运行时中执行,表达式和 Edit Fields 则在进程内运行;对于相同逻辑,Code 每次调用的开销可能高出数百倍(据使用者反馈,约为 2ms 对比 600ms)。对于热点路径和大量条目,这种差距会不断累积。

强烈建议的默认做法

  1. Code 节点是最后的选择。 决策顺序:表达式({{...}})→ Edit Fields 中的箭头函数 → Code 节点。前两种方式足以处理大多数“转换这些数据”的任务。只有在需要聚合多个来源、使用外部库,以及处理下文记录的少数特定模式时,才适合使用 Code。

  2. 默认使用 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 技能。

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.

使用 Code 和 require('crypto') 的模式,是最常见的“这需要用 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.

剩下适合使用 Code 处理加密的情况: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 节点中的 bug,请参见 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 节点的正文栏位支持表达式将表达式内联到电子邮件节点中
Code 节点使用 new Date() 格式化日期不如 Luxon 清晰在表达式中使用 Luxon。参见 n8n-expressions
Set 节点 + Code 节点组合(Set 构建输入,Code 进行转换)本应使用一个 Edit Fields,却用了两个节点合并为一个带箭头函数的 Edit Fields
将凭据/令牌粘贴到 Code 节点文本中与文本字段一样会造成泄露使用凭据,而非 Code 节点

评论

支持 Markdown