别只用现成节点了,你的业务逻辑才是核心竞争力
在 N8N 大学的后台私信里,笔者最常听到的一句话是:“这个需求 n8n 原生节点好像做不到。”
比如,你想把客户发来的复杂 PDF 里的特定条款提取出来,再根据条款算个违约金;又或者,你想调用一个不支持 Webhook 的老旧 ERP 系统数据。这时候,纯拖拽的自动化流程就像被蒙住了眼睛,有力使不出。
这就是为什么我们需要**自定义节点(Custom Nodes)**。它不是什么高不可攀的黑科技,而是让你的 n8n 实现“完全体”的关键钥匙。今天,笔者就带你从零开始,手把手写一个能用的自定义节点,并进阶到处理复杂逻辑。
准备工作:磨刀不误砍柴工
在开始写代码之前,我们需要搭建一个开发环境。别慌,不需要你是全栈大神,只需几个基础工具:
- Node.js 环境:建议安装最新的 LTS 版本(v18 或 v20+),这是运行 JavaScript 的基础。
- 代码编辑器:VS Code 是首选,插件丰富,调试方便。
- n8n 实例:本地安装的 n8n 开发环境最佳(使用
npm run dev启动),方便热更新调试。如果你是用 Docker,建议在本地映射一个开发目录。
自定义节点本质上就是一个符合 n8n 接口规范的 TypeScript/JavaScript 类。我们不需要从零搭建项目结构,n8n 官方提供了一个非常棒的脚手架工具。
核心实操:开发你的第一个“计算器”节点
我们不搞虚的,直接做一个实用的功能:**“两数相加并格式化输出”**。虽然逻辑简单,但它涵盖了自定义节点的所有核心概念:输入、处理、输出。
第一步:初始化项目结构
打开终端,运行以下命令初始化项目:
npx create-n8n-node
脚手架会询问你一些基本信息,比如节点名称(例如 n8n-nodes-custom-math)和描述。全部回车默认即可。完成后,你会得到一个标准的项目文件夹。
进入目录,安装依赖:
cd n8n-nodes-custom-math
npm install
第二步:编写节点逻辑代码
在 nodes 文件夹下,找到 CustomMath.ts(或类似名称)的文件。这是我们的主战场。我们需要修改 execute 方法,这里是数据处理的逻辑所在。
我们需要接收 Input Item,获取两个参数(num1 和 num2),相加后,写入一个新的字段 output。
import {
IExecuteFunctions,
INodeExecutionData,
INodeType,
INodeTypeDescription,
} from 'n8n-workflow';
export class CustomMath implements INodeType {
description: INodeTypeDescription = {
displayName: 'Custom Math',
name: 'customMath',
icon: 'file:math.svg',
group: ['transform'],
version: 1,
description: '简单的加法计算器',
defaults: {
name: 'Custom Math',
color: '#1A82e2',
},
inputs: ['main'],
outputs: ['main'],
properties: [
// 这里定义 UI 上的参数输入框
{
displayName: 'Number 1',
name: 'num1',
type: 'number',
default: 0,
required: true,
},
{
displayName: 'Number 2',
name: 'num2',
type: 'number',
default: 0,
required: true,
},
],
};
async execute(this: IExecuteFunctions) {
const items = this.getInputData();
let item: INodeExecutionData;
// 遍历每一行数据
for (let i = 0; i < items.length; i++) {
try {
// 获取用户在界面上填写的参数
const num1 = this.getNodeParameter('num1', i) as number;
const num2 = this.getNodeParameter('num2', i) as number;
// 核心逻辑:加法运算
const result = num1 + num2;
// 构造输出数据
// 复制原有数据,并在 json 字段中添加 result
item = {
json: {
...items[i].json,
result: result,
},
};
// 将结果推送到输出流
if (!this.shouldContinueExecution(items[i])) {
return [items.slice(0, i)];
}
items[i] = item;
} catch (error) {
// 简单的错误处理
if (this.continueOnFail()) {
items[i] = { json: { error: error.message } };
continue;
}
throw error;
}
}
return this.prepareOutputData(items);
}
}
第三步:编译与注册
代码写好了,我们需要把它编译成 n8n 能识别的格式。在项目根目录运行:
npm run build
编译成功后,你会在 dist 目录下看到生成的文件。接下来,我们需要让 n8n 加载这个节点。
如果你是本地开发环境,可以直接把整个项目文件夹复制到 n8n 的自定义节点目录(通常是 ~/.n8n/custom)。如果是 Docker 环境,你需要在 docker-compose.yml 中映射卷:
volumes:
- ./custom-nodes:/home/node/.n8n/custom
重启 n8n 服务后,打开编辑器界面,在节点搜索框输入 "Custom Math",你会发现我们的新节点已经躺在那里了。拖拽出来,填入两个数字,运行,见证奇迹的时刻。
进阶实战:处理复杂数据与错误
刚才的加法只是入门。在实际工作中,我们需要处理更复杂的场景,比如 API 调用、文件读取或批量数据处理。这里有两个进阶技巧。
1. 异步处理与 API 调用
很多自定义节点需要调用外部 API。这时候必须使用 async/await。
假设我们要调用一个天气 API。在 execute 方法中,我们需要使用 await this.helpers.httpRequest(...)。这是 n8n 提供的内置 HTTP 客户端,它自动处理了认证、Header 等细节。
const response = await this.helpers.httpRequest({
method: 'GET',
url: `https://api.example.com/weather?city=${city}`,
headers: {
'Authorization': `Bearer ${token}`
}
});
item.json.weather = response;
记住,n8n 的节点是单线程逻辑,但在高并发下,正确处理 Promise 链能避免内存泄漏。
2. 完善的错误处理(避坑指南)
很多新手开发的节点一报错就直接卡死整个工作流。这是一个巨大的坑。请务必使用 try...catch 包裹你的核心逻辑,并利用 n8n 的 continueOnFail 机制。
在上面的加法代码中,我已经展示了基础写法。在进阶版中,你应该在 catch 块里返回更详细的错误信息,而不是简单的 error.message。
避坑细节: 当你使用 continueOnFail 时,输出的数据结构必须保持一致。如果你在正常情况下输出 { json: { result: 1 } },那么在错误情况下,你也应该输出 { json: { error: "..." } }。否则,下游节点可能会因为字段缺失而报错。
如何发布与分享你的节点
当你开发完一个通用的节点(比如对接了某个特定 API),不要只在本地吃灰。N8N 大学鼓励大家开源分享。
- 发布到 NPM:运行
npm publish。这样全世界的 n8n 用户都可以通过npm install <你的包名>来使用。 - 供内部使用:将编译后的
dist文件夹打包,分发给团队成员,让他们放入自定义节点目录即可。
发布到 NPM 是标准做法,但如果你的节点包含敏感逻辑(如硬编码的密钥),请务必使用环境变量(process.env.MY_KEY)来管理。
FAQ:常见问题解答
在 N8N 大学的社区里,关于自定义节点开发的常见疑问如下:
Q1: 我不会 TypeScript,能用 JavaScript 开发吗?
可以。虽然官方推荐 TypeScript(类型检查更安全),但 n8n 底层运行在 Node.js 环境。你可以直接写 JavaScript 代码,只需要确保导出的类符合 n8n 的接口规范即可。不过,为了减少运行时错误,笔者强烈建议至少学一点 TS 的基础。
Q2: 自定义节点能访问本地文件系统吗?
能。因为 n8n 运行在 Node.js 环境中,你可以使用 fs 模块直接读写服务器上的文件。但请注意,如果你使用的是 Docker 部署,需要正确配置卷映射(Volume Mapping),否则文件写入后在容器重启时会丢失。
Q3: 为什么我的节点在界面上显示不出来?
通常有三个原因:
1. 编译失败:检查 npm run build 是否有报错。
2. 路径错误:确保编译后的文件位于 ~/.n8n/custom 或 Docker 映射的目录中。
3. 缓存问题:尝试重启 n8n 服务(pm2 restart n8n 或重启 Docker 容器)。
总结与资源
自定义节点开发是 n8n 进阶的必经之路。它让你摆脱了平台的限制,能够将任何复杂的业务逻辑封装成一个可复用的黑盒。从简单的数学计算,到对接私有 API,再到处理二进制文件,自定义节点的能力边界就是你想象力的边界。
不要害怕写代码。从今天开始,尝试把你工作流中重复出现的逻辑抽离出来,写成一个自定义节点。这不仅能提升效率,更是你掌握自动化核心能力的体现。
参考资源:
- n8n 官方节点开发文档:docs.n8n.io
- N8N 大学 GitHub 模板库:github.com/n8ndx