Code节点是n8n中最强大的功能之一。它允许你直接编写JavaScript或Python代码,来实现标准节点无法完成的复杂数据处理。无论是数据清洗、格式转换,还是自定义业务逻辑,Code节点都能胜任。
Code节点是n8n工作流中的一个代码执行器。它的核心作用是:
为什么要用Code节点?
console.log,可直接查看执行日志| 语言 | 运行环境 | 优势 | 适用场景 |
|---|---|---|---|
| JavaScript | Node.js | 原生速度快、文档丰富 | 绝大多数场景(推荐) |
| Python (Pyodide) | WebAssembly | 轻量级,无需额外配置 | 简单数据处理、字符串操作 |
| Python (Native) | 任务运行器(v1.111.0+) | 支持第三方库(pandas、numpy) | 高性能数据分析 |
本教程重点讲解JavaScript,因为它是最稳定、最快速的选择。
这是默认模式。无论输入有多少条数据,代码只执行一次,但可以处理所有项。
适用场景:
访问数据的方式:
// 获取所有项的数组
const allItems = $input.all();
// 获取第一项
const firstItem = $input.first();
// 获取最后一项
const lastItem = $input.last();返回示例:
return items.map(item => ({
json: {
id: item.json.id,
processed: true
}
}));代码会对每一条输入数据执行一次,逐个处理。
适用场景:
访问数据的方式:
// 获取当前处理的项
const currentItem = $input.item;
// 直接修改当前项
item.json.newField = "value";
// 返回修改后的当前项(不需要.map())
return item;模式对比表:
| 比较项 | Run Once for All | Run Once for Each |
|---|---|---|
| 执行次数 | 1次 | N次(N=输入项数) |
| 使用变量 | $input.all() | $input.item |
| 返回格式 | 数组 | 单个对象 |
| 性能 | 更快(处理复杂逻辑) | 更快(简单逐项处理) |
在n8n中,每条数据都是一个项(item),结构如下:
{
json: {
// 实际的业务数据存储在这里
id: 123,
name: "张三",
email: "zhangsan@example.com"
},
binary: {
// 存储二进制数据(如文件、图片)
// 通常情况下为空
}
}规则:必须返回包含json键的对象或对象数组
✅ 正确的返回格式:
// 格式1:单个项(逐项处理模式)
return {
json: {
name: "张三",
status: "active"
}
};
// 格式2:项数组(一次处理所有项模式)
return [
{ json: { id: 1, name: "张三" } },
{ json: { id: 2, name: "李四" } }
];
// 格式3:保留二进制数据
return {
json: { title: "文件" },
binary: { /* 二进制数据 */ }
};❌ 错误的返回格式:
// 错误1:直接返回字符串
return "success"; // ❌ 不行!
// 错误2:返回没有json的对象
return { name: "张三" }; // ❌ 不行!
// 错误3:返回原始值
return 123; // ❌ 不行!在Code节点中,n8n预设了许多变量供你直接使用:[10]
| 变量 | 说明 | 示例 |
|---|---|---|
$input.all() | 获取所有输入项 | $input.all()[0] |
$input.item | 获取当前项(逐项模式) | $input.item.json.id |
$input.first() | 获取第一项 | $input.first().json.name |
$input.last() | 获取最后一项 | $input.last().json.email |
$json | 当前项的JSON数据(简写) | $json.name |
$now | 当前时间(Luxon对象) | $now.toISO() |
$today | 今天日期(Luxon对象) | $today.toFormat("YYYY-MM-DD") |
$env | 环境变量 | $env["API_KEY"] |
$workflow.name | 当前工作流名称 | 用于日志记录 |
$execution.id | 当前执行ID | 用于追踪 |
场景:收到用户数据,需要合并姓氏和名字为完整名字,并规范化邮箱格式。
return items.map(item => {
const json = item.json;
return {
json: {
id: json.id,
// 合并姓名
fullName: `${json.firstName} ${json.lastName}`,
// 邮箱小写并去空白
email: json.email.toLowerCase().trim(),
// 保留原始数据
originalFirstName: json.firstName,
originalLastName: json.lastName
}
};
});场景:只保留"活跃"状态的用户,去除不需要的数据。
return items
.filter(item => item.json.status === 'active') // 过滤条件
.map(item => ({
json: {
id: item.json.id,
name: item.json.name,
email: item.json.email
// 注意:其他字段被丢弃
}
}));场景:订单数据处理,计算每项的小计,并添加处理时间戳。
return items.map(item => {
const json = item.json;
const quantity = parseInt(json.quantity);
const unitPrice = parseFloat(json.price);
return {
json: {
orderId: json.orderId,
productName: json.productName,
quantity: quantity,
unitPrice: unitPrice,
// 计算小计
subtotal: quantity * unitPrice,
// 添加处理时间戳
processedAt: new Date().toISOString(),
// 简单的状态判断
status: quantity > 100 ? 'bulk_order' : 'regular_order'
}
};
});场景:验证数据,如果缺少必要字段则标记为错误。
return items.map(item => {
const json = item.json;
// 验证必要字段
const hasError = !json.email || !json.name;
return {
json: {
...json, // 保留所有原始字段
// 添加验证结果
isValid: !hasError,
error: hasError ? '缺少必要字段: email 或 name' : null,
// 基于条件的字段
priority: json.amount > 1000 ? 'high' : 'normal'
}
};
});场景:一个订单包含多个产品,需要将其展开为多条记录。
let resultItems = [];
items.forEach(item => {
const json = item.json;
// 如果包含产品数组,展开
if (Array.isArray(json.products)) {
json.products.forEach(product => {
resultItems.push({
json: {
orderId: json.orderId,
customerName: json.customerName,
productId: product.id,
productName: product.name,
quantity: product.quantity,
price: product.price
}
});
});
} else {
// 如果没有产品数组,直接传递
resultItems.push(item);
}
});
return resultItems;场景:获取当前时间,计算用户注册天数。
return items.map(item => {
const json = item.json;
// 解析注册日期(假设为ISO字符串)
const registrationDate = new Date(json.registrationDate);
const now = new Date();
// 计算天数差
const daysRegistered = Math.floor(
(now - registrationDate) / (1000 * 60 * 60 * 24)
);
return {
json: {
...json,
daysRegistered: daysRegistered,
// 基于天数的用户等级
userLevel: daysRegistered > 365 ? 'VIP' : 'Regular',
// 添加当前时间戳
currentTimestamp: $now.toISO()
}
};
});// 打印完整的输入数据
console.log('Complete input:', JSON.stringify($input.all(), null, 2));
// 打印单个项的结构
console.log('First item:', $input.first().json);
// 打印中间结果
const processed = items.map(item => {
console.log('Processing item:', item.json.id);
return item;
});
return processed;查看日志:在n8n工作流执行后,点击Code节点查看执行面板,会显示所有console.log输出。
| 错误 | 原因 | 解决方案 |
|---|---|---|
Cannot read property 'json' of undefined | 访问了不存在的数据 | 使用$input.first()先检查是否有数据 |
return value must be array | 在All Items模式返回单个对象 | 确保返回 [{json: {...}}] 数组 |
json property is required | 缺少json字段 | 所有返回的对象必须包含json键 |
SyntaxError: Unexpected token | JavaScript语法错误 | 检查括号、逗号、分号 |
以下是一个完整的、可直接导入到n8n中的工作流,该工作流演示了Code节点的综合应用:
场景:处理CSV用户数据,进行清洗、验证、分类,最终输出规范化的JSON。
{
"name": "用户数据清洗与分类工作流",
"nodes": [
{
"parameters": {},
"id": "1a2b3c4d",
"name": "Start",
"type": "n8n-nodes-base.start",
"typeVersion": 1,
"position": [250, 300]
},
{
"parameters": {
"mode": "runOnceForAllItems",
"jsCode": "// 模拟原始CSV数据(实际项目中来自前一个节点)\nconst sampleData = [\n { firstName: 'Zhang', lastName: 'San', email: 'zhangsan@example.com', age: 28, status: 'active' },\n { firstName: 'Li', lastName: 'Si', email: 'LISI@EXAMPLE.COM', age: 35, status: 'inactive' },\n { firstName: 'Wang', lastName: 'Wu', email: 'wangwu@example.com', age: 22, status: 'active' },\n { firstName: 'Zhao', lastName: 'Liu', email: 'zhaoliu@example.com', age: 45, status: 'active' }\n];\n\n// 步骤1:清洗和转换数据\nconst cleanedData = sampleData.map(user => {\n // 合并姓名\n const fullName = `${user.firstName} ${user.lastName}`;\n \n // 规范化邮箱(转小写,去空白)\n const normalizedEmail = user.email.toLowerCase().trim();\n \n // 解析年龄\n const age = parseInt(user.age);\n \n // 基于年龄的分类\n let ageGroup = 'adult';\n if (age < 18) ageGroup = 'minor';\n else if (age >= 60) ageGroup = 'senior';\n \n return {\n json: {\n fullName: fullName,\n email: normalizedEmail,\n age: age,\n ageGroup: ageGroup,\n isActive: user.status === 'active',\n registeredDate: $now.toISO(),\n // VIP判断:年龄在30-50之间,且状态活跃\n isVIP: age >= 30 && age <= 50 && user.status === 'active'\n }\n };\n});\n\n// 步骤2:过滤(只保留活跃用户)\nconst activeUsers = cleanedData.filter(item => item.json.isActive);\n\nconsole.log('Cleaned:', cleanedData.length, 'users');\nconsole.log('Active:', activeUsers.length, 'users');\n\nreturn activeUsers;"
},
"id": "2b3c4d5e",
"name": "Code - 数据清洗与分类",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [500, 300]
},
{
"parameters": {
"keepOnlySet": false,
"values": {
"string": [],
"number": [],
"boolean": [],
"dateTime": [],
"object": [],
"json": [
{
"name": "processedData",
"value": "={{ $json }}"
}
]
},
"options": {}
},
"id": "3c4d5e6f",
"name": "Set - 最终输出",
"type": "n8n-nodes-base.set",
"typeVersion": 3,
"position": [750, 300]
}
],
"connections": {
"Start": {
"main": [
[
{
"node": "Code - 数据清洗与分类",
"type": "main",
"index": 0
}
]
]
},
"Code - 数据清洗与分类": {
"main": [
[
{
"node": "Set - 最终输出",
"type": "main",
"index": 0
}
]
]
}
}
}工作流会输出3条活跃用户的规范化数据:
[
{
"fullName": "Zhang San",
"email": "zhangsan@example.com",
"age": 28,
"ageGroup": "adult",
"isActive": true,
"isVIP": false,
"registeredDate": "2025-12-05T11:04:00.000Z"
},
{
"fullName": "Wang Wu",
"email": "wangwu@example.com",
"age": 22,
"ageGroup": "adult",
"isActive": true,
"isVIP": false,
"registeredDate": "2025-12-05T11:04:00.000Z"
},
{
"fullName": "Zhao Liu",
"email": "zhaoliu@example.com",
"age": 45,
"ageGroup": "adult",
"isActive": true,
"isVIP": true,
"registeredDate": "2025-12-05T11:04:00.000Z"
}
]undefined错误[29]if (item.json.email && item.json.email.trim()) {
// 处理邮箱
}try {
const result = JSON.parse(item.json.data);
} catch (error) {
console.log('Parse error:', error);
item.json.parseError = error.message;
}const email = item.json.email || 'unknown@example.com';console.log('Debug point:', JSON.stringify(item, null, 2));// ❌ 不可靠
let counter = 0; // 这会被重置// ❌ 不行
const fs = require('fs');// ❌ 危险
for (let i = 0; i < 1000000; i++) { }Q1:Code节点和表达式(Expressions)有什么区别?
A:
{{ $json.name }},用于简单的数据映射和计算Q2:如何访问前一个节点的数据?
A:使用$input.all()或$input.first()可以获取前一个节点的输出。如果需要访问更早的节点,使用:
const previousNodeData = $('节点名称').first().json;Q3:Code节点有性能限制吗?
A:
filter()和map()替代Q4:能在Code节点中使用npm包吗?
A:
Q5:如何调试Code节点中的错误?
A:
console.log()打印中间结果try-catch捕获异常并打印详细信息Code节点的核心要点:
| 要点 | 说明 |
|---|---|
| 两种模式 | All Items(一次处理所有)/ Each Item(逐项处理) |
| 数据格式 | 必须返回{json: {...}}格式 |
| 核心变量 | $input.all() / $input.item / $json / $now |
| 常用操作 | filter过滤、map转换、条件判断、数据合并 |
| 调试方法 | console.log输出、查看执行日志 |
| 性能 | JavaScript > Python,适合中等数据量 |
掌握Code节点,你就掌握了n8n最强大的功能。从简单的字段映射到复杂的数据处理流程,Code节点都能帮你实现。
祝你的自动化之旅顺利!🚀
[1] 官方文档: https://docs.n8n.io/code/code-node/
[2] n8n系列教程: https://www.undsky.com/blog/?category=n8n%E6%95%99%E7%A8%8B#