在 N8N大学,我们见过太多朋友被“标准节点”的能力边界卡住。明明 API 文档写得很清楚,但 n8n 官方仓库里就是没有对应的节点。这时候,很多人就放弃了,退回到手动复制粘贴的低效循环里。
其实,n8n 最强大的地方恰恰在于它的“开放性”。**自定义节点(Custom Nodes)** 不是高手的玩具,而是每个想彻底解放双手的自动化玩家的必备技能。今天,笔者就带你手把手从零开始,开发一个属于你自己的 n8n 节点,并完成实战部署。
一、场景导入:为什么你需要自定义节点?
假设你是一个电商运营,每天需要从一个不知名的 ERP 系统里拉取订单状态。这个 ERP 只提供了 API 接口,没有现成的 n8n 节点。你只能用 HTTP Request 节点,然后手动拼接 URL、解析 JSON,每次都要重新配置参数。
更痛苦的是,当你把这个工作流分享给同事时,他们面对复杂的配置一脸茫然。如果你能把它封装成一个节点,同事只需要填入“订单号”就能直接使用,这才是自动化的终极形态——封装复杂,简化使用。
二、准备工作:磨刀不误砍柴工
在开始之前,你需要准备好以下环境。别担心,门槛并不高:
- Node.js 环境:建议安装 v16 或 v18 版本(n8n 的核心依赖版本)。
- n8n CLI:确保你已经安装了 n8n,能在命令行输入
n8n。 - 代码编辑器:VS Code 是首选,配合 TypeScript 插件体验更佳。
- 一个简单的 API 目标:为了演示,笔者会用一个公开的免费测试 API(如 JSONPlaceholder)来模拟获取用户数据。
三、核心实操:从脚手架到第一个节点
别从零开始写文件结构,n8n 官方提供了一个极好的脚手架工具。跟着我的步骤走。
步骤 1:使用 CLI 创建项目骨架
打开终端,执行以下命令:
npx n8n-node-dev new
系统会提示你输入节点的名称(如 n8n-nodes-custom-demo)和描述。这会自动帮你生成一个标准的 TypeScript 项目结构,包含 package.json 和 nodes 目录。这一步能帮你避开依赖地狱。
步骤 2:编写节点逻辑 (credentials.ts)
大多数 API 都需要鉴权。在生成的 credentials 文件夹中,新建一个 MyApi.credentials.ts。这里我们以简单的 API Key 为例:
import { ICredentialType, INodeProperties } from 'n8n-workflow';
export class MyApi implements ICredentialType {
name = 'myApi';
displayName = 'My API';
properties: INodeProperties[] = [
{
displayName: 'API Key',
name: 'apiKey',
type: 'string',
typeOptions: { password: true }, // 这里让输入框显示为密码样式
default: '',
},
];
}
记住这个 name,稍后在节点里会引用它。
步骤 3:编写节点逻辑 (nodes.ts)
这是最核心的部分。打开 nodes 文件夹下的文件,我们需要定义节点的描述(Description)和执行逻辑(Execute)。
1. 定义节点描述:告诉 n8n 这个节点长什么样,有哪些输入框。
interface INodeProperties[] = [
{
displayName: 'User ID',
name: 'userId',
type: 'string',
default: '1',
required: true,
description: '要获取的用户ID',
}
];
2. 编写执行逻辑:在 execute 方法中,我们发起 HTTP 请求。
async execute(this: IExecuteFunctions) {
const credentials = await this.getCredentials('myApi'); // 获取上面配置的 API Key
const userId = this.getNodeParameter('userId', 0) as string;
// 使用 n8n 内置的请求工具
const response = await this.helpers.httpRequest({
method: 'GET',
url: `https://jsonplaceholder.typicode.com/users/${userId}`,
headers: {
'Authorization': `Bearer ${credentials.apiKey}`
}
});
return [this.helpers.returnJsonArray(response)];
}
这段代码的逻辑很直白:拿到参数 -> 拼接 URL -> 发起请求 -> 返回结果。
步骤 4:本地调试与运行
在项目根目录下运行:
npm run dev
你会看到终端提示节点正在监听文件变化。此时,打开你的 n8n 界面(如果是 Docker 安装的,需要确保节点文件挂载到了容器内,或者使用 docker exec 复制进去)。刷新页面,你就能在节点列表里搜到你刚才开发的节点了!
四、实战部署:让你的节点被全世界使用
开发完成只是第一步,怎么让团队成员或者客户也能用?
方法 1:打包为 npm 包 (推荐)
这是最标准的生产环境做法。执行 npm run build 编译代码,然后在项目目录下运行 npm publish(需要先注册 npm 账号)。发布后,其他人只需要在 n8n 的设置 -> 节点安装中搜索你的包名即可一键安装。
方法 2:Docker 镜像集成
如果你是在私有化部署 n8n,可以直接将编译后的 dist 文件夹挂载到 Docker 容量中。在 docker-compose.yml 中添加卷映射:
volumes:
- ./custom-nodes/dist:/home/node/.n8n/custom
重启容器,节点即可生效。这种方法适合企业内部快速迭代,不需要去 npm 发包。
五、避坑指南:实战中的血泪史
作为 N8N大学的主编,我必须提醒你注意以下几个新手最容易踩的坑:
1. TypeScript 类型地狱
n8n 基于 TypeScript 开发,很多方法返回的是 any。在编写代码时,不要偷懒省略类型定义。特别是在处理 this.getNodeParameter() 时,如果类型不对,编译时不会报错,但运行时会导致节点直接崩溃,且报错信息非常晦涩。
2. 节点图标与色彩
默认的节点图标很丑,且在列表里难以辨识。在节点描述中,务必配置 icon 属性。你可以使用 FontAwesome 的图标代码,或者 Base64 编码的 SVG。一个好看的图标能让你在复杂的工作流中迅速定位节点。
3. 错误处理不完善
新手代码往往只处理了 200 OK 的情况。在 execute 方法中,务必用 try...catch 包裹请求逻辑。如果 API 报错,应该抛出清晰的 NodeOperationError,而不是让 n8n 抛出通用的 "Execution failed"。
六、FAQ 常见问题
Q1: 我不懂 TypeScript,能开发 n8n 节点吗?
A: 严格来说比较困难。n8n 的 SDK 是强类型的。但你可以先用 JavaScript 编写,然后在 tsconfig.json 中关闭严格模式。不过,笔者建议花半天时间学习 TypeScript 基础,这会让你的开发事半功倍。
Q2: 开发的节点可以商用吗?
A: 当然可以!n8n 采用 Faircode 许可证,允许你开发私有节点并用于商业目的,也不强制开源你的代码。
Q3: 为什么我的节点在 n8n 界面中显示红色错误?
A: 通常是因为 package.json 中的 n8n 字段版本不匹配,或者节点代码中有语法错误。打开 n8n 的日志(Docker logs 或控制台输出),通常能看到具体的报错堆栈。
七、总结与资源
自定义节点开发是 n8n 进阶的必经之路。它不仅能解决标准节点无法覆盖的边缘场景,更能让你积累一套可复用的自动化资产。不要害怕 TypeScript,从修改一个现成的开源节点开始,逐步替换其中的逻辑。
如果你在开发过程中遇到棘手的 API 集成问题,欢迎随时访问 N8N大学 (n8ndx.com),这里有更多硬核的实战教程等你探索。