第 9 期:代码是最终兵器 — Code 节点编写 JS/Python
Code 节点的定位
当可视化节点无法满足你的需求(如复杂的数据结构转换、正则匹配、数学计算等),Code 节点就是你的最终兜底。
graph TB
subgraph "n8n 处理能力阶梯"
L1[🟢 内置节点
Set / If / Merge
覆盖 70% 场景]
L2[🟡 表达式
{{ $json.field }}
覆盖 20% 场景]
L3[🔴 Code 节点
JS / Python
覆盖剩余 10%]
end
L1 --> L2 --> L3
style L3 fill:#ff6d5b,stroke:#e55a4e,color:#fff1. 两种运行模式
🔄 Run Once for All Items(默认推荐)
一次性获取所有 Items,你自己控制遍历逻辑。
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 模式: "Run Once for All Items"
// 特点: $input.all() 返回全部 Items,你自己决定如何处理
// 适用: 聚合计算、排序、去重、分组等需要看到"全局"的场景
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
const allItems = $input.all(); // 获取全部 Items
// 示例: 按价格从高到低排序
const sorted = allItems.sort((a, b) => b.json.price - a.json.price);
// 示例: 去重 (按 email 字段)
const seen = new Set();
const unique = allItems.filter(item => {
if (seen.has(item.json.email)) return false; // 已见过,过滤掉
seen.add(item.json.email); // 标记为已见
return true; // 保留
});
// 必须返回 Item 数组
return unique;
🔁 Run Once for Each Item
n8n 自动遍历,每个 Item 独立执行一次代码。
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 模式: "Run Once for Each Item"
// 特点: $input.item 是"当前这一个" Item
// 适用: 对每条数据做独立转换(无需看到其他 Items)
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
const item = $input.item; // 当前正在处理的 Item
// 示例: 手机号脱敏
const phone = item.json.phone;
const masked = phone.slice(0, 3) + '****' + phone.slice(-4);
// 13812345678 → 138****5678
// 返回单个 Item(不是数组!)
return {
json: {
...item.json, // 保留原有字段
maskedPhone: masked, // 新增脱敏字段
processedAt: new Date().toISOString() // 新增时间戳
}
};
2. 内置变量与引用
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// n8n Code 节点内置变量速查表
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 📌 当前节点数据
$input.all() // 所有输入 Items (Run Once for All)
$input.first() // 第一个 Item
$input.last() // 最后一个 Item
$input.item // 当前 Item (Run Once for Each)
$input.all().length // Items 总数
// 📌 跨节点引用 (⭐ 极其强大!)
$('Set').all() // 获取 "Set" 节点的全部输出 Items
$('HTTP Request').first().json // 获取 "HTTP Request" 节点第一条数据的 JSON
$('Telegram Trigger').first().json.message.text // 深度跨节点引用
// 📌 工作流元信息
$execution.id // 当前执行 ID
$execution.mode // "manual" | "trigger"
$workflow.id // 工作流 ID
$workflow.name // 工作流名称
// 📌 时间函数
$now // 当前时间 (Luxon DateTime 对象)
$now.toISO() // "2026-04-09T12:00:00.000+08:00"
$now.toFormat('yyyy-MM-dd') // "2026-04-09"
$today // 今天 00:00:00
// 📌 环境变量
$env.MY_SECRET_KEY // 读取 n8n 环境变量 (安全!)
3. 实战案例集
案例 1: CSV 文本解析
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 场景: 将一段 CSV 文本拆解为结构化 Items
// 输入: { "json": { "csvData": "姓名,年龄,城市\n张三,28,上海\n李四,35,北京" } }
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
const csv = $input.first().json.csvData;
const lines = csv.split('\n'); // 按换行符分割
const headers = lines[0].split(','); // 第一行是表头
// 跳过表头,将每行数据转为 Item
const items = lines.slice(1).map(line => {
const values = line.split(',');
const obj = {};
headers.forEach((header, index) => {
obj[header.trim()] = values[index]?.trim(); // 对齐表头与值
});
return { json: obj };
});
return items;
// 输出:
// [
// { "json": { "姓名": "张三", "年龄": "28", "城市": "上海" } },
// { "json": { "姓名": "李四", "年龄": "35", "城市": "北京" } }
// ]
案例 2: 数据分组统计
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 场景: 按城市分组,统计每个城市的订单数与总金额
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
const items = $input.all();
// 使用 reduce 做分组聚合
const grouped = items.reduce((acc, item) => {
const city = item.json.city;
if (!acc[city]) {
acc[city] = { city, count: 0, totalAmount: 0 }; // 初始化分组
}
acc[city].count += 1; // 计数 +1
acc[city].totalAmount += item.json.amount; // 累加金额
return acc;
}, {});
// 将分组结果转为 Item 数组输出
return Object.values(grouped).map(group => ({ json: group }));
// 输出:
// [
// { "json": { "city": "上海", "count": 15, "totalAmount": 28500 } },
// { "json": { "city": "北京", "count": 8, "totalAmount": 12000 } }
// ]
案例 3: 异步操作(使用 await)
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// n8n Code 节点默认支持 async/await!
// 场景: 在 Code 节点中直接发起 HTTP 请求 (无需 HTTP Request 节点)
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// ⚠️ 注意: n8n Code 节点使用 Node.js 沙箱
// 内置 fetch API 可直接使用
const response = await fetch('https://api.github.com/repos/n8n-io/n8n');
const data = await response.json();
return [{
json: {
repoName: data.full_name, // "n8n-io/n8n"
stars: data.stargazers_count, // 50000+
language: data.language, // "TypeScript"
lastUpdate: data.updated_at
}
}];
4. Python 模式 (n8n 2.0+)
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# n8n Code 节点 Python 模式
# 需要在节点设置中将 Language 切换为 "Python"
# ⚠️ 需要 N8N_RUNNERS_ENABLED=true (Task Runner)
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# 获取所有输入 Items
items = _input.all()
# 使用 Python 列表推导式进行数据处理
result = []
for item in items:
data = item["json"] # 访问 Item 的 json 字段
# 中文姓名 → 拼音 (示例逻辑)
processed = {
"original": data["name"],
"uppercased": data["name"].upper(),
"length": len(data["name"]),
}
result.append({"json": processed})
return result
安全注意事项
graph TB
subgraph "Code 节点安全模型"
Code[💻 你的代码]
Code -->|"✅ 允许"| Allow1[fetch / HTTP 请求]
Code -->|"✅ 允许"| Allow2[JSON 处理]
Code -->|"✅ 允许"| Allow3[数学计算]
Code -->|"❌ 禁止"| Deny1[访问文件系统 fs]
Code -->|"❌ 禁止"| Deny2[require 外部模块]
Code -->|"❌ 禁止"| Deny3[子进程 child_process]
end
Note[🛡️ Task Runner 沙箱
N8N_RUNNERS_ENABLED=true]
Code -.-> Note
style Note fill:#22c55e,stroke:#16a34a,color:#fff⚠️ 安全提示: 启用
N8N_RUNNERS_ENABLED=true后,Code 节点在隔离的 Task Runner 沙箱中执行,无法访问宿主文件系统或执行系统命令,有效防止代码注入攻击。
下一步
在 Ep 10 中,我们将快速串烧 n8n 最实用的内置工具节点——Merge、Set、Edit Image、Date & Time 等,补齐日常开发的最后拼图。