【n8n教程】:掌握Code节点,释放自定义代码的力量

Code节点是n8n中最强大的功能之一。它允许你直接编写JavaScript或Python代码,来实现标准节点无法完成的复杂数据处理。无论是数据清洗、格式转换,还是自定义业务逻辑,Code节点都能胜任。


第一部分:认识Code节点

1.1 Code节点是什么?

Code节点是n8n工作流中的一个代码执行器。它的核心作用是:

为什么要用Code节点?

  1. 1. 提高效率:一个Code节点可以替代10-15个标准节点的功能[10]
  2. 2. 降低复杂度:使工作流更加清晰、易于维护
  3. 3. 灵活性强:完全由你控制数据处理的逻辑
  4. 4. 调试便捷:支持console.log,可直接查看执行日志

1.2 Code节点支持的语言

语言运行环境优势适用场景
JavaScriptNode.js原生速度快、文档丰富绝大多数场景(推荐)
Python (Pyodide)WebAssembly轻量级,无需额外配置简单数据处理、字符串操作
Python (Native)任务运行器(v1.111.0+)支持第三方库(pandas、numpy)高性能数据分析

本教程重点讲解JavaScript,因为它是最稳定、最快速的选择。


第二部分:Code节点的两种执行模式

2.1 模式一:Run Once for All Items(一次处理所有项)

这是默认模式。无论输入有多少条数据,代码只执行一次,但可以处理所有项。

适用场景

访问数据的方式


    
    
    
  // 获取所有项的数组
const
 allItems = $input.all();

// 获取第一项

const
 firstItem = $input.first();

// 获取最后一项

const
 lastItem = $input.last();

返回示例


    
    
    
  return items.map(item => ({
  json
: {
    id
: item.json.id,
    processed
: true
  }
}));

2.2 模式二:Run Once for Each Item(逐项处理)

代码会对每一条输入数据执行一次,逐个处理。

适用场景

访问数据的方式


    
    
    
  // 获取当前处理的项
const
 currentItem = $input.item;

// 直接修改当前项

item.json.newField = "value";

// 返回修改后的当前项(不需要.map())

return
 item;

模式对比表

比较项Run Once for AllRun Once for Each
执行次数1次N次(N=输入项数)
使用变量$input.all()$input.item
返回格式数组单个对象
性能更快(处理复杂逻辑)更快(简单逐项处理)

第三部分:核心概念与数据结构

3.1 n8n中的数据结构

在n8n中,每条数据都是一个项(item),结构如下:


    
    
    
  {
  json
: {
    // 实际的业务数据存储在这里

    id
: 123,
    name
: "张三",
    email
: "zhangsan@example.com"
  },
  binary
: {
    // 存储二进制数据(如文件、图片)

    // 通常情况下为空

  }
}

3.2 Code节点中必须返回的数据格式

规则:必须返回包含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;  // ❌ 不行!

3.3 n8n内置的关键变量

在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用于追踪

第四部分:实战案例 - JavaScript Code节点示例

案例1:数据清洗与字段合并

场景:收到用户数据,需要合并姓氏和名字为完整名字,并规范化邮箱格式。


    
    
    
  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
    }
  };
});

案例2:数据过滤

场景:只保留"活跃"状态的用户,去除不需要的数据。


    
    
    
  return items
  .filter(item => item.json.status === 'active')  // 过滤条件
  .map(item => ({
    json
: {
      id
: item.json.id,
      name
: item.json.name,
      email
: item.json.email
      // 注意:其他字段被丢弃

    }
  }));

案例3:数据转换与计算

场景:订单数据处理,计算每项的小计,并添加处理时间戳。


    
    
    
  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'
    }
  };
});

案例4:条件逻辑与错误处理

场景:验证数据,如果缺少必要字段则标记为错误。


    
    
    
  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'
    }
  };
});

案例5:数据的展开(Exploding)

场景:一个订单包含多个产品,需要将其展开为多条记录。


    
    
    
  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;

案例6:使用内置方法与时间处理

场景:获取当前时间,计算用户注册天数。


    
    
    
  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()
    }
  };
});

第五部分:调试技巧

5.1 使用console.log进行调试


    
    
    
  // 打印完整的输入数据
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输出。

5.2 常见错误排查

错误原因解决方案
Cannot read property 'json' of undefined访问了不存在的数据使用$input.first()先检查是否有数据
return value must be array在All Items模式返回单个对象确保返回 [{json: {...}}] 数组
json property is required缺少json字段所有返回的对象必须包含json键
SyntaxError: Unexpected tokenJavaScript语法错误检查括号、逗号、分号

第六部分:完整可执行工作流案例

以下是一个完整的、可直接导入到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
          }

        ]

      ]

    }

  }

}

如何导入这个工作流?

  1. 1. 打开n8n编辑器
  2. 2. 点击左上角菜单Import from FileImport from URL
  3. 3. 复制上述JSON代码,粘贴到导入框
  4. 4. 点击导入
  5. 5. 点击执行按钮,即可看到数据清洗的结果

预期结果:

工作流会输出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"
  }

]

第七部分:最佳实践建议

✅ 做这些事

  1. 1. 始终验证输入数据:检查字段是否存在,避免undefined错误[29]
    
        
        
        
      if (item.json.email && item.json.email.trim()) {
      // 处理邮箱

    }
  2. 2. 使用try-catch处理异常
    
        
        
        
      try {
      const
     result = JSON.parse(item.json.data);
    } catch (error) {
      console
    .log('Parse error:', error);
      item.json.parseError = error.message;
    }
  3. 3. 为字段添加默认值
    
        
        
        
      const email = item.json.email || 'unknown@example.com';
  4. 4. 使用console.log调试:它是你的好朋友
    
        
        
        
      console.log('Debug point:', JSON.stringify(item, null, 2));

❌ 避免这些事

  1. 1. 不要依赖全局变量:每次执行Code节点时,作用域是隔离的
    
        
        
        
      // ❌ 不可靠
    let
     counter = 0;  // 这会被重置
  2. 2. 不要尝试访问文件系统:Code节点无法读写磁盘
    
        
        
        
      // ❌ 不行
    const
     fs = require('fs');
  3. 3. 不要进行超长的循环:会导致超时
    
        
        
        
      // ❌ 危险
    for
     (let i = 0; i < 1000000; i++) { }
  4. 4. 不要忘记返回正确格式:必须返回包含json的对象数组

常见问题解答(FAQ)

Q1:Code节点和表达式(Expressions)有什么区别?

A:

Q2:如何访问前一个节点的数据?

A:使用$input.all()$input.first()可以获取前一个节点的输出。如果需要访问更早的节点,使用:


    
    
    
  const previousNodeData = $('节点名称').first().json;

Q3:Code节点有性能限制吗?

A:

Q4:能在Code节点中使用npm包吗?

A:

Q5:如何调试Code节点中的错误?

A:

  1. 1. 使用console.log()打印中间结果
  2. 2. 在n8n执行后,查看Code节点的执行日志
  3. 3. 使用try-catch捕获异常并打印详细信息

总结

Code节点的核心要点

要点说明
两种模式All Items(一次处理所有)/ Each Item(逐项处理)
数据格式必须返回{json: {...}}格式
核心变量$input.all() / $input.item / $json / $now
常用操作filter过滤、map转换、条件判断、数据合并
调试方法console.log输出、查看执行日志
性能JavaScript > Python,适合中等数据量

掌握Code节点,你就掌握了n8n最强大的功能。从简单的字段映射到复杂的数据处理流程,Code节点都能帮你实现。

祝你的自动化之旅顺利!🚀


官方文档[1]
n8n系列教程[2]

引用链接

[1] 官方文档: https://docs.n8n.io/code/code-node/
[2] n8n系列教程: https://www.undsky.com/blog/?category=n8n%E6%95%99%E7%A8%8B#