在 n8n 中自开发组件(自定义节点)是一个强大的功能,可以让您扩展平台的能力。以下是完整的开发指南:
1. 开发环境准备
安装 n8n CLI
bash复制代码
npm install -g @n8n/n8n
# 或
npm install -g n8n
创建自定义节点项目
bash复制代码
# 创建项目目录
mkdir n8n-nodes-custom
cd n8n-nodes-custom
# 初始化项目
npm init -y
2. 项目结构
复制代码
n8n-nodes-custom/
├── nodes/ # 自定义节点目录
│ ├── MyCustomNode/
│ │ ├── MyCustomNode.node.ts
│ │ └── MyCustomNode.node.json
├── package.json
├── tsconfig.json
└── webpack.config.js
3. 基础配置文件
package.json
json复制代码
{
"name": "n8n-nodes-custom",
"version": "1.0.0",
"description": "Custom n8n nodes",
"main": "index.js",
"scripts": {
"build": "webpack --mode=production",
"dev": "webpack --watch --mode=development"
},
"dependencies": {
"n8n-workflow": "^0.28.0"
},
"devDependencies": {
"@types/node": "^16.0.0",
"ts-loader": "^9.2.6",
"typescript": "^4.5.5",
"webpack": "^5.68.0",
"webpack-cli": "^4.9.2"
}
}
tsconfig.json
json复制代码
{
"compilerOptions": {
"target": "es2020",
"lib": ["es2020"],
"module": "commonjs",
"moduleResolution": "node",
"declaration": true,
"outDir": "./dist",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["nodes/**/*"]
}
4. 创建自定义节点
节点定义文件 (MyCustomNode.node.json)
json复制代码
{
"node": {
"version": 1,
"name": "myCustomNode",
"displayName": "My Custom Node",
"description": "A custom node example",
"defaults": {
"name": "My Custom Node"
},
"inputs": ["main"],
"outputs": ["main"],
"properties": [
{
"displayName": "Resource",
"name": "resource",
"type": "options",
"noDataExpression": true,
"options": [
{
"name": "User",
"value": "user"
}
],
"default": "user"
},
{
"displayName": "Operation",
"name": "operation",
"type": "options",
"noDataExpression": true,
"options": [
{
"name": "Get",
"value": "get"
}
],
"default": "get"
},
{
"displayName": "User ID",
"name": "userId",
"type": "string",
"default": "",
"required": true,
"displayOptions": {
"show": {
"resource": ["user"],
"operation": ["get"]
}
}
}
]
}
}
节点逻辑文件 (MyCustomNode.node.ts)
typescript复制代码
import {
IExecuteFunctions,
INodeExecutionData,
INodeType,
INodeTypeDescription,
} from 'n8n-workflow';
export class MyCustomNode implements INodeType {
description: INodeTypeDescription = {
displayName: 'My Custom Node',
name: 'myCustomNode',
icon: 'fa:user',
group: ['transform'],
version: 1,
description: 'A custom node example',
defaults: {
name: 'My Custom Node',
color: '#FF6B6B',
},
inputs: ['main'],
outputs: ['main'],
properties: [
{
displayName: 'Resource',
name: 'resource',
type: 'options',
options: [
{
name: 'User',
value: 'user',
},
],
default: 'user',
required: true,
description: 'The resource to operate on',
},
{
displayName: 'Operation',
name: 'operation',
type: 'options',
options: [
{
name: 'Get',
value: 'get',
},
],
default: 'get',
required: true,
},
{
displayName: 'User ID',
name: 'userId',
type: 'string',
default: '',
required: true,
displayOptions: {
show: {
resource: ['user'],
operation: ['get'],
},
},
description: 'The ID of the user',
},
],
};
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
const items = this.getInputData();
const returnData: INodeExecutionData[] = [];
const resource = this.getNodeParameter('resource', 0) as string;
const operation = this.getNodeParameter('operation', 0) as string;
for (let i = 0; i < items.length; i++) {
try {
if (resource === 'user' && operation === 'get') {
const userId = this.getNodeParameter('userId', i) as string;
// 这里实现你的业务逻辑
const userData = {
id: userId,
name: `User ${userId}`,
email: `user${userId}@example.com`,
timestamp: new Date().toISOString(),
};
returnData.push({
json: userData,
pairedItem: {
item: i,
},
});
}
} catch (error) {
if (this.continueOnFail()) {
returnData.push({
json: {
error: error.message,
},
pairedItem: {
item: i,
},
});
continue;
}
throw error;
}
}
return [returnData];
}
}
5. Webpack 配置
webpack.config.js
javascript复制代码
const path = require('path');
module.exports = {
target: 'node',
entry: './nodes/MyCustomNode/MyCustomNode.node.ts',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'MyCustomNode.node.js',
libraryTarget: 'commonjs2',
},
resolve: {
extensions: ['.ts', '.js'],
},
module: {
rules: [
{
test: /\.ts$/,
use: 'ts-loader',
exclude: /node_modules/,
},
],
},
externals: {
'n8n-workflow': 'commonjs2 n8n-workflow',
},
};
6. 构建和安装
构建节点
bash复制代码
npm run build
在 n8n 中安装
- 将构建好的文件复制到 n8n 的 custom extensions 目录
- 或使用 npm 包方式安装
7. 高级功能示例
认证配置
typescript复制代码
// 在 properties 数组中添加认证配置
{
displayName: 'Authentication',
name: 'authentication',
type: 'options',
options: [
{
name: 'API Key',
value: 'apiKey',
},
{
name: 'OAuth2',
value: 'oauth2',
},
],
default: 'apiKey',
required: true,
}
Webhook 支持
typescript复制代码
// 添加 webhook 描述
webhooks: [
{
name: 'default',
httpMethod: 'POST',
responseMode: 'onReceived',
path: 'webhook',
},
],
8. 测试和调试
本地测试
bash复制代码
# 启动 n8n 并加载自定义节点
n8n start --tunnel
调试技巧
- 使用
console.log输出调试信息 - 检查 n8n 日志文件
- 使用 try-catch 处理错误
9. 最佳实践
- 错误处理: 实现完整的错误处理机制
- 类型安全: 使用 TypeScript 确保类型安全
- 文档: 为节点提供清晰的文档说明
- 测试: 编写单元测试和集成测试
- 版本控制: 遵循语义化版本控制
这个指南涵盖了 n8n 自定义节点开发的主要方面。根据您的具体需求,可以进一步扩展和定制节点的功能。