n8n-mcp 实战:n8n Code 节点 JavaScript 数据访问模式权威指南

发布时间:2026/9/13 8:31:05
n8n-mcp 实战:n8n Code 节点 JavaScript 数据访问模式权威指南 n8n-mcp 实战n8n Code 节点 JavaScript 数据访问模式权威指南【免费下载链接】n8n-mcpA MCP for Claude Desktop / Claude Code / Windsurf / Cursor to build n8n workflows for you项目地址: https://gitcode.com/GitHub_Trending/n8/n8n-mcp本篇技术指南以 n8n-mcp 仓库中 DATA_ACCESS.md 为骨架系统讲解在 n8n Code 节点中用 JavaScript 读取上游数据的全部方式$input.all()、$input.first()、$input.item、$node的适用场景、完整代码示例、Webhook.body嵌套陷阱、执行模式性能对比与生产级注意事项并配合仓库内代码校验器node-specific-validators.ts与测试用例佐证底层行为。读完本文你将能根据批量聚合 / 单条处理 / 逐项独立 / 跨节点引用四种诉求准确选型写出可复制、可运行、可上生产的 Code 节点代码。一、数据访问方式全景优先级与适用场景n8n Code 节点通过内置变量与方法读取前序节点的数据。选错访问方式是工作流执行失败的常见根源先记住整体优先级按常见使用频率排序使用率数据出自 README.md 的统计优先级访问方式使用率参考典型场景1$input.all()最常见约 26%批量操作、聚合计算2$input.first()很常见约 25%单条数据处理3$input.item常见约 19%仅 Run Once for Each Item 模式4$node[NodeName].json特定场景引用指定命名节点5$json遗留方式直接访问当前项建议改用$input从仓库源码看Code 节点校验器默认把模式视为runOnceForAllItemsconst mode config.mode || runOnceForAllItems见 node-specific-validators.ts并据此校验返回格式与变量用法——这说明All Items 模式是默认且推荐的执行方式不仅是文档建议也是校验器的默认前提。二、Pattern 1$input.all()—— 批量处理与聚合用途Code 节点中最常用的批量处理模式。适用场景处理多条记录数据聚合求和、计数、平均值数组过滤数据集转换条目间比较排序或排名基本用法// 获取前序节点的全部条目 const allItems $input.all(); // allItems 是形如以下结构的对象数组 // [ // {json: {id: 1, name: Alice}}, // {json: {id: 2, name: Bob}} // ] console.log(Received ${allItems.length} items); return allItems;示例 1过滤有效条目const allItems $input.all(); // 只保留 status 为 active 的条目 const activeItems allItems.filter(item item.json.status active); return activeItems;示例 2转换全部条目const allItems $input.all(); // 映射为新结构 const transformed allItems.map(item ({ json: { id: item.json.id, fullName: ${item.json.firstName} ${item.json.lastName}, email: item.json.email, processedAt: new Date().toISOString() } })); return transformed;示例 3聚合数据const allItems $input.all(); // 计算总额 const total allItems.reduce((sum, item) { return sum (item.json.amount || 0); }, 0); return [{ json: { total, count: allItems.length, average: total / allItems.length } }];示例 4排序并截取前 N 条const allItems $input.all(); // 按 score 取前 5 名 const topFive allItems .sort((a, b) (b.json.score || 0) - (a.json.score || 0)) .slice(0, 5); return topFive.map(item ({json: item.json}));示例 5按类别分组const allItems $input.all(); // 按 category 分组 const grouped {}; for (const item of allItems) { const category item.json.category || Uncategorized; if (!grouped[category]) { grouped[category] []; } grouped[category].push(item.json); } // 转换为数组格式 return Object.entries(grouped).map(([category, items]) ({ json: { category, items, count: items.length } }));示例 6按 ID 去重const allItems $input.all(); // 按 ID 去除重复 const seen new Set(); const unique []; for (const item of allItems) { const id item.json.id; if (!seen.has(id)) { seen.add(id); unique.push(item); } } return unique;源码佐证校验器对代码不引用任何输入数据的检查Code doesnt reference input data明确把items、$input、$json、$node等列为合法输入访问模式并给出提示Access input with: items, $input.all(), or $json (single-item mode)见 node-specific-validators.ts。这意味着$input.all()是被校验器认可的第一推荐入口。三、Pattern 2$input.first()—— 获取首条数据用途单条数据操作的常用模式。适用场景前序节点返回单个对象处理 API 响应获取首个/初始数据点读取配置或元数据基本用法// 获取前序节点的第一条数据 const firstItem $input.first(); // 访问 JSON 数据 const data firstItem.json; console.log(First item:, data); return [{json: data}];示例 1处理单条 API 响应// 获取 API 响应通常为单个对象 const response $input.first().json; // 提取所需字段 return [{ json: { userId: response.data.user.id, userName: response.data.user.name, status: response.status, fetchedAt: new Date().toISOString() } }];示例 2转换单个对象结构const data $input.first().json; // 重构结构 return [{ json: { id: data.id, contact: { email: data.email, phone: data.phone }, address: { street: data.street, city: data.city, zip: data.zip } } }];示例 3校验单条数据const item $input.first().json; // 校验逻辑 const isValid item.email item.email.includes(); return [{ json: { ...item, valid: isValid, validatedAt: new Date().toISOString() } }];示例 4提取嵌套数据const response $input.first().json; // 深入嵌套结构可选链安全访问 const users response.data?.users || []; return users.map(user ({ json: { id: user.id, name: user.profile?.name || Unknown, email: user.contact?.email || no-email } }));示例 5与其他方法组合// 取首条数据 const firstData $input.first().json; // 用它过滤全部条目 const allItems $input.all(); const matching allItems.filter(item item.json.category firstData.targetCategory ); return matching;注意示例 5 展示了$input.first()与$input.all()的协作——这是以首条数据作为过滤基准的常见业务形态也印证了两者并非互斥而是按需组合。四、Pattern 3$input.item—— Each Item 模式下的当前项用途仅用于 Run Once for Each Item逐项执行模式。适用场景节点模式设置为 Run Once for Each Item需要独立处理每一条数据逐项发起 API 调用或校验针对单项的错误处理⚠️IMPORTANT$input.item只能在 Each Item 模式下使用在 All Items 模式下为undefined。基本用法// 在 Run Once for Each Item 模式下 const currentItem $input.item; const data currentItem.json; console.log(Processing item:, data.id); return [{ json: { ...data, processed: true } }];示例 1追加处理元数据const item $input.item; return [{ json: { ...item.json, processed: true, processedAt: new Date().toISOString(), processingDuration: Math.random() * 1000 // 模拟耗时 } }];示例 2逐项校验const item $input.item; const data item.json; // 校验当前条目 const errors []; if (!data.email) errors.push(Email required); if (!data.name) errors.push(Name required); if (data.age data.age 18) errors.push(Must be 18); return [{ json: { ...data, valid: errors.length 0, errors: errors.length 0 ? errors : undefined } }];示例 3逐项 API 调用const item $input.item; const userId item.json.userId; // 针对当前条目发起 API 调用 const response await this.helpers.httpRequest({ method: GET, url: https://api.example.com/users/${userId}/details }); return [{ json: { ...item.json, details: response } }];⚠️务必使用this.helpers.httpRequest而不是$helpers。在 Code 节点的 task-runner 沙箱n8n v2.0 起默认中裸的$helpers全局变量是undefined——$helpers.httpRequest()会抛出ReferenceError: $helpers is not defined。不要为此扩展带认证的调用模式this.helpers.httpRequestWithAuthentication在 task-runner 沙箱中被列入拒绝名单deny-list调用会抛UnsupportedFunctionError。带认证的请求应改用挂载了凭证的HTTP Request 节点或委托给持有凭证的子工作流。除了一次简单的匿名 GET其余场景都更推荐直接用 HTTP Request 节点。详见 ERROR_PATTERNS.md 的 Error #6。示例 4条件处理const item $input.item; const data item.json; // 按条目类型处理 if (data.type premium) { return [{ json: { ...data, discount: 0.20, tier: premium } }]; } else { return [{ json: { ...data, discount: 0.05, tier: standard } }]; }源码佐证校验器对模式与变量用法的联动检查印证了本节约束——当config.mode runOnceForEachItem且代码中出现items数组时给出警告In Run Once for Each Item mode, use $json instead of items array反之当未设置模式却使用$json时警告$json only works in Run Once for Each Item mode见 node-specific-validators.ts。可见$input.item/$json与 Each Item 模式是强绑定关系混用即产生行为偏差。五、Pattern 4$node—— 引用其他命名节点用途使用频率较低但在特定场景下非常强大。适用场景需要读取指定命名节点的数据组合多个节点的数据访问工作流执行的元信息基本用法// 获取指定节点的输出 const webhookData $node[Webhook].json; const apiData $node[HTTP Request].json; return [{ json: { fromWebhook: webhookData, fromAPI: apiData } }];示例 1组合多个数据源// 引用多个节点 const webhook $node[Webhook].json; const database $node[Postgres].json; const api $node[HTTP Request].json; return [{ json: { combined: { webhook: webhook.body, dbRecords: database.length, apiResponse: api.status }, processedAt: new Date().toISOString() } }];示例 2跨节点数据对比const oldData $node[Get Old Data].json; const newData $node[Get New Data].json; // 对比差异 const changes { added: newData.filter(n !oldData.find(o o.id n.id)), removed: oldData.filter(o !newData.find(n n.id o.id)), modified: newData.filter(n { const old oldData.find(o o.id n.id); return old JSON.stringify(old) ! JSON.stringify(n); }) }; return [{ json: { changes, summary: { added: changes.added.length, removed: changes.removed.length, modified: changes.modified.length } } }];示例 3访问节点执行路径元数据// 获取特定执行分支的数据 const ifTrueBranch $node[IF True].json; const ifFalseBranch $node[IF False].json; // 使用实际执行的那个分支 const result ifTrueBranch || ifFalseBranch || {}; return [{json: result}];语法提示使用$(NodeName)函数式引用时必须调用.first().json或.all()不能直接在引用上取.json即$(HTTP Request).json是错误的正确写法是$(HTTP Request).first().json。这是 DATA_ACCESS.md Production Gotchas 明确列出的易错点详见下文生产环境注意事项。六、关键陷阱Webhook 数据嵌套在.body下最常见错误忘记 Webhook 数据被包裹在.body属性下。Webhook 节点把所有入站数据统一放进body字段这让许多开发者措手不及。数据结构// Webhook 节点输出结构 { headers: { content-type: application/json, user-agent: ..., // ... 其他请求头 }, params: {}, query: {}, body: { // ← 你的数据在这里 name: Alice, email: aliceexample.com, message: Hello! } }错误 vs 正确写法// ❌ WRONG: 试图直接访问 const name $json.name; // undefined const email $json.email; // undefined // ✅ CORRECT: 通过 .body 访问 const name $json.body.name; // Alice const email $json.body.email; // aliceexample.com // ✅ CORRECT: 先提取 body const webhookData $json.body; const name webhookData.name; // Alice const email webhookData.email; // aliceexample.com完整 Webhook 处理示例// 从前序节点获取 webhook 数据 const webhookOutput $input.first().json; // 访问真正的负载 const payload webhookOutput.body; // 按需访问请求头 const contentType webhookOutput.headers[content-type]; // 按需访问查询参数 const apiKey webhookOutput.query.api_key; // 处理真实数据 return [{ json: { // webhook body 中的数据 userName: payload.name, userEmail: payload.email, message: payload.message, // 元数据 receivedAt: new Date().toISOString(), contentType: contentType, authenticated: !!apiKey } }];POST 数据、查询参数与请求头const webhook $input.first().json; return [{ json: { // POST body 数据 formData: webhook.body, // 查询参数?keyvalue queryParams: webhook.query, // HTTP 请求头 userAgent: webhook.headers[user-agent], contentType: webhook.headers[content-type], // 请求元信息 method: webhook.method, // POST, GET 等 url: webhook.url } }];常见 Webhook 业务场景速写// 场景 1表单提交 const formData $json.body; const name formData.name; const email formData.email; // 场景 2JSON API webhook const apiPayload $json.body; const eventType apiPayload.event; const data apiPayload.data; // 场景 3查询参数 const apiKey $json.query.api_key; const userId $json.query.user_id; // 场景 4请求头 const authorization $json.headers[authorization]; const signature $json.headers[x-signature];源码与测试佐证.body陷阱在仓库中同时被文档、校验器、测试三层固化。校验器在识别到 webhook 相关代码时发出警告Webhook data is nested under .body property并建议Use items[0].json.body.fieldName instead of items[0].json.fieldName for webhook data见 node-specific-validators.ts 附近逻辑对应测试用例位于 node-specific-validators.test.ts覆盖直接访问 payload 字段引用了 Webhook 节点代码中出现 webhook 字样三种触发形态示例生成器也内置了const webhookData items[0].json.body的标准写法见 example-generator.test.ts。把.body当作访问 Webhook 数据的强制约定是降低工作流失败率的头号措施。七、如何选择正确的数据访问方式决策树需要前序节点的全部条目 ├─ 是 → 使用 $input.all() │ └─ 否 → 只需要第一条 ├─ 是 → 使用 $input.first() │ └─ 否 → 处于 Each Item 模式 ├─ 是 → 使用 $input.item │ └─ 否 → 需要特定节点的数据 ├─ 是 → 使用 $node[NodeName] └─ 否 → 使用 $input.first()默认快速参考表场景使用方式示例金额求和$input.all()allItems.reduce((sum, i) sum i.json.amount, 0)获取 API 响应$input.first()$input.first().json.data逐条独立处理$input.item$input.item.jsonEach Item 模式组合两个节点$node[Name]$node[API].json过滤数组$input.all()allItems.filter(i i.json.active)转换单个对象$input.first(){...input.first().json, new: true}Webhook 数据$input.first()$input.first().json.body八、五大常见错误与规避错误 1脱离上下文使用$json// ❌ WRONG: $json 语义模糊 const value $json.field; // ✅ CORRECT: 显式表达 const value $input.first().json.field;错误 2忘记.json属性// ❌ WRONG: 直接在条目对象上取字段 const items $input.all(); const names items.map(item item.name); // undefined // ✅ CORRECT: 通过 .json 访问 const names items.map(item item.json.name);错误 3在 All Items 模式下使用$input.item// ❌ WRONG: All Items 模式下 $input.item 是 undefined const data $input.item.json; // Error! // ✅ CORRECT: 改用合适的方法 const data $input.first().json; // 或 $input.all()错误 4不处理空数组// ❌ WRONG: 无条目时直接崩溃 const first $input.all()[0].json; // ✅ CORRECT: 先检查长度 const items $input.all(); if (items.length 0) { return []; } const first items[0].json; // ✅ ALSO CORRECT: 使用 $input.first() const first $input.first().json; // 内置安全性错误 5修改原始数据// ❌ RISKY: 原地修改原始数据 const items $input.all(); items[0].json.modified true; // 污染了原始条目 return items; // ✅ SAFE: 创建新对象 const items $input.all(); return items.map(item ({ json: { ...item.json, modified: true } }));错误 4 补充说明校验器专门实现了顶层 return 检测Code must return data for the next node见 node-specific-validators.ts会剥离注释、字符串与嵌套函数体后判断是否存在真实顶层return同时校验器认可$input.first()这种内置安全写法——它比裸的$input.all()[0]更稳健是仓库推荐的默认单条访问方式。九、高级模式模式一分页处理const currentPage $input.all(); const pageNumber $node[Set Page].json.page || 1; // 与之前页面合并 const allPreviousPages $node[Accumulator]?.json.accumulated || []; return [{ json: { accumulated: [...allPreviousPages, ...currentPage], currentPage: pageNumber, totalItems: allPreviousPages.length currentPage.length } }];模式二条件化节点引用// 根据条件访问不同节点 const condition $input.first().json.type; let data; if (condition api) { data $node[API Response].json; } else if (condition database) { data $node[Database].json; } else { data $node[Default].json; } return [{json: data}];模式三多节点聚合// 从多个命名节点收集数据 const sources [Source1, Source2, Source3]; const allData []; for (const source of sources) { const nodeData $node[source]?.json; if (nodeData) { allData.push({ source, data: nodeData }); } } return allData.map(item ({json: item}));十、模式性能为什么 All Items 更快执行模式的选择是 Code 节点中最大的性能杠杆其原理可以推广到整个工作流n8n 每把一个条目交给一个逐项执行上下文都要支付一次上下文构建开销。以下数据在 n8n 2.x 实例上以小型记录、约 1 万条数据实测每项执行的内容近似成本原因CodeAll Items整批只运行一次~0.02 ms/条只构建一次上下文之后是纯 JS——循环本身零成本任意节点中的表达式IF / Set 等~0.2 ms/条每个条目一个轻量 eval 上下文CodeEach Item~0.6 ms/条每个条目一个完整代码沙箱——约为表达式的 3 倍、All Items 的 25–30 倍因此对 1 万条数据使用 Run Once for Each Item 会产生约 6 秒纯开销而同样的逻辑放进 Run Once for All Items 只需约 0.2 秒。只有当条目确实需要隔离独立错误处理或无法批量化的逐项 API 调用时才用 Each Item否则请在一个 All Items 节点内部循环。两个高频推论表达式复杂度基本是免费的一个复杂的{{ }}与一个平凡表达式的耗时几乎相同——约 90% 的成本花在 n8n 构建逐项上下文上而不是执行你的代码。不要为了速度去简化表达式而应减少逐项边界的数量。每个节点间的跳转都会重新拷贝全部条目约 0.05 ms/条/跳。六个串联的 All Items Code 节点成本约为一个完成同样六步的单节点 7 倍——请把热点转换链合并进一个 All Items 节点并且永远不要构建 Each Item Code 节点的链条逐项税会乘以节点数6 节点 Each Item 链处理 2 千条数据约需 7 秒。规模检查条目数在几百以内时上述差异都在亚 100ms 级别不必在意而且大多数真实工作流耗时被 I/OHTTP / DB / Sheets 往返主导远大于节点开销。请只在热点路径条目量大、I/O 少应用这些规则而非处处套用。十一、生产环境注意事项Production Gotchas来自真实 n8n 工作流部署的实战经验。SplitInBatches 循环语义SplitInBatches 节点有两个输出且命名容易引起误解main[0]done完成—— 所有批次处理完后只触发一次main[1]each batch每个批次—— 每个批次都会触发这才是循环体务必在 done 输出之后、下游处理之前加一个Limit 1节点作为安全阀防止 done 在边缘情况下携带多余条目触发。SplitInBatches迭代次数就是成本每次循环迭代都会让工作流引擎重新执行整个循环体——每迭代约 0.8 ms 纯开销叠加循环体自身的成本。总成本 ≈⌈items / batchSize⌉ × (~0.8 ms body cost)对 N 条数据用batchSize: 1就要付 N 次迭代开销——这是循环版的 Run Once for Each Item如果循环体里有多个节点每次迭代还会重复支付全部节点成本。提高batchSize按比例减少迭代次数循环体仍然处理每条数据。请使用真实约束允许的最大批次API 限流、页大小、内存。如果根本不需要分批就不要循环——在一个 All Items 节点里处理整批数据。跨迭代数据累积CRITICALSplitInBatches 循环结束后$(Node Inside Loop).all()只返回最后一次迭代的条目而不是累计结果。这会静默丢弃除最后一批外的所有数据。修复方案使用工作流静态数据跨迭代累积// 循环开始前重置累加器 const staticData $getWorkflowStaticData(global); staticData.results []; return $input.all(); // 循环体内累积 const staticData $getWorkflowStaticData(global); const results []; for (const item of $input.all()) { const processed { /* ... */ }; results.push({ json: processed }); staticData.results.push(processed); } return results; // 循环结束后读取累积数据 const staticData $getWorkflowStaticData(global); const allResults staticData.results || []; // 现在可以对所有迭代的结果做聚合为新增输出条目补充 pairedItem当创建与输入条目非 1:1 对应的新条目时必须带上pairedItem否则下游 Set 节点会报paired_item_no_infoconst results []; for (let i 0; i $input.all().length; i) { const item $input.all()[i]; results.push({ json: { /* 新数据 */ }, pairedItem: { item: i } }); } return results;正确的节点引用语法// ❌ WRONG - 直接在节点引用上取 .json const data $(HTTP Request).json; // ✅ CORRECT - 先调用 .first() 再取 .json const data $(HTTP Request).first().json; // ✅ Also correct - 获取全部条目 const allData $(HTTP Request).all();价格/货币比较的浮点精度问题比较价格或货币值时浮点噪声可能导致误判。请四舍五入到分再比较// ❌ Unreliable - 浮点比较 if (newPrice ! oldPrice) { /* 噪声触发误判 */ } // ✅ Reliable - 在分级别比较 if (Math.round(newPrice * 100) ! Math.round(oldPrice * 100)) { // 检测到真实的价格变化 }十二、结语把数据访问规范固化为默认习惯回顾全文n8n Code 节点 JavaScript 数据访问的核心可以浓缩为三条规则默认 All Items $input.all()批量、聚合、过滤、排序、去重等绝大多数场景用一个 All Items 节点内部循环解决性能最好、语义最清晰按需选用$input.first()/$input.item/$node单条处理用$input.first()逐项隔离才用 Each Item 模式下的$input.item跨节点组合才用$node[NodeName]牢记两个硬约定Webhook 数据永远在.body下返回格式永远用规范的[{json: {...}}]。这些约定不只是文档建议——仓库的 node-specific-validators.ts 已把它们实现为可执行的校验逻辑模式识别、顶层 return 检测、{{ }}语法拦截、webhook.body警告并有 node-specific-validators.test.ts 等测试兜底。当你通过 n8n-mcp 的validate_node()工具提交 Code 节点配置时这些检查会直接作用于你的代码帮助你提前暴露问题。更多生产级代码形态可继续阅读同目录下的 COMMON_PATTERNS.md10 个实战模式与 ERROR_PATTERNS.md错误速查表。【免费下载链接】n8n-mcpA MCP for Claude Desktop / Claude Code / Windsurf / Cursor to build n8n workflows for you项目地址: https://gitcode.com/GitHub_Trending/n8/n8n-mcp创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考