n8n 二开入门:如何从零编写一个自定义节点 (Declarative Style)?

2026-01-27 14 0

场景导入:为什么你需要学会“造轮子”?

兄弟们,我是 N8N大学 的主编。咱们玩 n8n 的,最怕什么?不是流程太复杂,而是市面上的节点不够用。比如你想对接一个冷门的 API,或者想封装一个公司内部的特殊逻辑,翻遍了官方社区和 GitHub,就是找不到现成的节点。

这时候,很多人就卡住了,只能用 HTTP Request 节点在那里拼 JSON,维护起来简直是噩梦。其实,n8n 最硬核的地方就在于它的开放性。学会编写自定义节点,就像给你的自动化航母装上了自研的导弹系统。今天,笔者就带你从零开始,用 Declarative(声明式)风格手撕一个自定义节点。这绝对是小白进阶大神的必经之路。

准备工作:磨刀不误砍柴工

在开始之前,咱们得先把环境搭好。别慌,不需要什么高深的前端技术栈,但你需要以下工具:

  • Node.js:确保你本地安装了 Node.js (建议 v16 或更高版本)。
  • 代码编辑器:VS Code 是笔者的首选,写 TypeScript 体验极佳。
  • n8n 二开环境:你需要把 n8n 的源码克隆到本地。直接装在全局的 npm 版本是没法开发的。去 GitHub 把 n8n 的 repo clone 下来,然后 npm install

笔者提示: 如果你只是想快速体验,可以直接使用 N8N大学 提供的开发模板,而不是全量克隆 n8n 源码,这样启动速度会快很多。但为了学习原理,建议先跑通一次官方源码流程。

核心实操:手写一个“日期格式化”节点

咱们今天不写复杂的 CRUD,而是写一个实用的工具节点:输入一个时间戳,输出指定格式的日期字符串。这能让你彻底搞懂 Declarative 节点的生命周期。

第一步:创建节点骨架

在 n8n 源码的 packages 目录下,找到 nodes-base。所有的官方节点都在这里。为了不污染源码,我们可以在同级新建一个文件夹 custom-nodes,或者直接在 nodes-base 里的 nodes 目录下新建一个文件夹,比如 DateFormatter

在这个文件夹里,我们需要创建两个核心文件:

  • DateFormatter.node.ts:节点的主逻辑文件。
  • index.ts:注册文件,告诉 n8n 这里有个新节点。

第二步:定义节点描述 (Declarative 核心)

Declarative Style 的精髓在于“描述”。你不是在写一堆命令式代码告诉程序怎么做,而是定义一份 JSON 配置,告诉 n8n “这个节点长什么样,有哪些输入输出”。

DateFormatter.node.ts 中,我们继承 Node 类,并实现 describe 方法。这部分代码决定了 UI 面板长什么样:

// 伪代码示例,帮助理解结构
export class DateFormatter extends Node {
    describe() {
        return {
            displayName: '日期格式化器',
            name: 'dateFormatter',
            icon: 'file:date.svg', // 需要准备图标文件
            group: ['transform'],
            version: 1,
            description: '将时间戳转换为可读日期',
            defaults: {
                name: '日期格式化',
                color: '#00FF00',
            },
            inputs: ['main'],
            outputs: ['main'],
            properties: [
                {
                    displayName: '输入格式',
                    name: 'inputFormat',
                    type: 'options',
                    options: [
                        { name: 'Unix 时间戳 (秒)', value: 'unix' },
                        { name: 'Unix 时间戳 (毫秒)', value: 'unixMs' },
                    ],
                    default: 'unixMs',
                    description: '选择你输入数据的时间戳格式',
                },
                {
                    displayName: '输出格式',
                    name: 'outputFormat',
                    type: 'string',
                    default: 'YYYY-MM-DD HH:mm:ss',
                    placeholder: '例如: YYYY-MM-DD',
                }
            ]
        };
    }
}

看到没?properties 数组就是定义 UI 控件的地方。这里定义了下拉菜单和文本框,完全不需要写一行 HTML。

第三步:编写执行逻辑 (execute 方法)

定义好外观后,我们需要告诉 n8n 当用户点击“执行节点”时会发生什么。这需要实现 execute 方法。在这里,我们要获取用户在 UI 上填写的参数,并处理数据。

// 继续在同一个文件中
async execute() {
    // 1. 获取用户配置
    const inputFormat = this.getNodeParameter('inputFormat');
    const outputFormat = this.getNodeParameter('outputFormat');

    // 2. 遍历输入的每一项数据 (n8n 是批量处理的)
    const items = this.getInputData();
    const newItems = [];

    for (let i = 0; i < items.length; i++) {
        const item = items[i];
        
        // 假设我们要处理的字段叫 'timestamp'
        // 在实际开发中,这里需要做防御性编程,防止字段不存在报错
        let timestamp = item.json.timestamp;

        // 3. 数据处理逻辑
        if (inputFormat === 'unix') {
            timestamp = timestamp * 1000;
        }
        
        // 使用 dayjs 或原生 Date 进行格式化 (这里简化演示)
        const dateObj = new Date(timestamp);
        const formattedDate = dateObj.toISOString(); // 实际应按 outputFormat 格式化

        // 4. 输出数据
        newItems.push({
            json: {
                ...item.json, // 保留原数据
                formattedDate: formattedDate // 新增字段
            }
        });
    }

    // 5. 返回结果
    return this.prepareOutputData(newItems);
}

这就是 Declarative Style 的魅力:UI 定义和逻辑执行是分离的,但通过 this.getNodeParameter 紧密结合。

第四步:注册与调试

写好代码后,别忘了在 index.ts 中导出它:

import { DateFormatter } from './DateFormatter.node';
export { DateFormatter as DateFormatter };

然后在 n8n 的主注册文件中引入你的新节点。最后,运行 npm run dev。打开浏览器,搜索“日期格式化器”,如果看到了你的图标,恭喜你,二开的大门已经向你敞开!

避坑指南:二开路上的拦路虎

笔者当年写第一个节点时,踩了不少坑。这里分享两个最典型的,帮你省下两小时:

  1. 参数类型错误:execute 里用 this.getNodeParameter('name') 拿到的值,有时候是字符串,有时候是对象。特别是在处理表达式(比如 {{ $json.id }})时,n8n 会在运行时解析。如果你的逻辑依赖特定类型(比如数字),一定要在代码里做类型转换,比如 parseInt()Number()
  2. UI 变更未生效: 修改了 describe 方法里的属性后,刷新浏览器页面,发现节点配置面板没变?这是因为 n8n 缓存了旧的节点定义。这时候你需要清理浏览器缓存,或者更粗暴一点,删除根目录下的 node_modules/.cache 文件夹,然后重启开发服务。

FAQ:你可能遇到的疑惑

1. Declarative Style 和 Script 节点有什么区别?

Declarative Style 是开发“正规”节点的方式,它可以被打包、分发、版本管理,且性能更好。而 Script 节点(JS/Python)适合快速原型验证或一次性逻辑。如果你打算长期复用某个逻辑,或者想把它发布到 npm 上给别人用,必须用 Declarative Style。

2. 我需要懂 Vue.js 才能开发节点吗?

完全不需要!虽然 n8n 的前端是 Vue 写的,但作为节点开发者,你只需要编写 TypeScript (后端逻辑) 和一份 JSON 描述 (UI 配置)。n8n 会自动帮你把 JSON 渲染成 Vue 组件。这就是声明式的威力。

3. 自定义节点能商用吗?

当然可以。n8n 是开源的 (Fair-code 协议)。你可以基于它开发私有节点部署在自己的服务器上,也可以开发公开节点发布到社区。N8N大学 后续也会推出教程,教大家如何打包发布。

总结与资源

从零编写自定义节点,是 n8n 玩家从“使用者”蜕变为“造物主”的关键一步。Declarative Style 虽然一开始看有点抽象,但一旦掌握了“描述即代码”的思维,你会发现它的扩展性极强。

资源推荐:

  • n8n 官方文档 - Nodes Development 部分
  • N8N大学 GitHub 模板库 (后续更新)

我是 N8N大学 的主编,希望这篇硬核教程能帮你少走弯路。如果你在实操中遇到报错,欢迎在评论区留言,笔者会第一时间帮你排查。下期见!

相关文章

安全加固:限制 n8n Code 节点访问文件系统与内网 IP (Sandbox 配置)
超越 SQLite:为什么生产环境必须连接 PostgreSQL 以及如何配置?
高并发架构:配置 Redis + n8n Worker 模式实现分布式任务处理
解决 OOM 崩溃:优化 n8n 大数据量处理时的内存占用 (Memory Leak) 技巧
多入口触发:如何让一个工作流同时支持 GET/POST 甚至多个不同路径的 Webhook?
控制 n8n 本身:使用 n8n Public API 自动创建、激活或导出工作流

发布评论