Skip to main content

Custom Nodes

Custom Nodes sind selbst entwickelte Erweiterungen für Automate, die Sie in Ihre Workflows einbinden können. Sie ermöglichen es Ihnen, spezifische Integrationen, Transformationen oder Geschäftslogik zu implementieren, die nicht durch Standard-Nodes abgedeckt wird. Mit Custom Nodes erweitern Sie Automate um eigene, benutzerdefinierte Nodes, die genau auf Ihre Anforderungen zugeschnitten sind.

Spezifische Integrationen

Verbinden Sie sich mit internen Systemen oder spezialisierten APIs.

Wiederverwendbare Logik

Kapseln Sie komplexe Geschäftslogik in wiederverwendbaren Nodes.

Performance-Optimierung

Optimieren Sie spezifische Operationen für Ihre Anwendungsfälle.

Team-spezifische Features

Entwickeln Sie Nodes, die speziell für Ihr Team oder Ihre Organisation sind.

Wann Custom Nodes erstellen?

Szenario: Sie müssen sich mit einer internen API verbinden, die nicht in der Standard-Node-Bibliothek verfügbar ist.Lösung: Erstellen Sie einen Custom Node, der die API-Aufrufe kapselt und die Daten im Automate-Format bereitstellt.
Szenario: Sie benötigen spezifische Daten-Transformationen, die über Standard-Nodes hinausgehen.Lösung: Entwickeln Sie einen Transformations-Node mit Ihrer spezifischen Logik.
Szenario: Bestimmte Geschäftsregeln werden in vielen Workflows verwendet.Lösung: Kapseln Sie diese Logik in einem Custom Node für einfache Wiederverwendung.

Node-Architektur

Grundlegende Struktur

Ein Custom Node besteht aus mehreren Komponenten:
interface INodeType {
  description: INodeTypeDescription;
  execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
  methods?: INodeTypeDescriptionMethods;
}

Node-Typen

Regular Node

Standard-Node:Führt Operationen aus und gibt Daten weiter. Wird von anderen Nodes aufgerufen.Beispiele: Daten-Transformation, API-Calls, Berechnungen

Trigger Node

Trigger-Node:Startet Workflows automatisch basierend auf Events oder Zeitplänen.Beispiele: Webhook-Trigger, Schedule-Trigger, Event-Listener

Webhook Node

Webhook-Node:Empfängt HTTP-Requests und kann Antworten senden.Beispiele: REST API Endpoints, Webhook-Listener

Entwicklungsumgebung einrichten

Voraussetzungen

  • Node.js 18+ installiert
  • TypeScript installiert (npm install -g typescript)
  • Git für Versionskontrolle
  • Code-Editor (VS Code empfohlen)

Projekt-Setup

1

Projekt-Verzeichnis erstellen

Erstellen Sie ein neues Verzeichnis für Ihren Custom Node:
mkdir localmind-custom-node
cd localmind-custom-node
2

package.json initialisieren

Erstellen Sie eine package.json Datei:
npm init -y
Bearbeiten Sie die package.json:
{
  "name": "localmind-custom-node",
  "version": "1.0.0",
  "description": "Custom Nodes für Localmind Automate",
  "main": "index.js",
  "scripts": {
    "build": "tsc",
    "dev": "tsc --watch"
  },
  "keywords": ["n8n", "n8n-node", "localmind"],
  "author": "Localmind",
  "license": "MIT",
  "n8n": {
    "n8nNodesApiVersion": 1,
    "nodes": [
      {
        "node": "LocalmindAgent",
        "sourcePath": "nodes/LocalmindAgent/LocalmindAgent.node.ts"
      }
    ]
  },
  "dependencies": {
    "n8n-workflow": "^1.0.0"
  },
  "devDependencies": {
    "@types/node": "^20.0.0",
    "typescript": "^5.0.0"
  }
}
3

TypeScript konfigurieren

Erstellen Sie tsconfig.json:
{
  "compilerOptions": {
    "module": "commonjs",
    "target": "ES2020",
    "lib": ["ES2020"],
    "declaration": true,
    "outDir": "./dist",
    "rootDir": "./",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "resolveJsonModule": true
  },
  "include": ["nodes/**/*"],
  "exclude": ["node_modules", "dist"]
}
4

Abhängigkeiten installieren

Installieren Sie die benötigten Pakete:
npm install

Erster Custom Node erstellen

Beispiel: Localmind Agent Node

Lassen Sie uns einen einfachen Custom Node erstellen, der Localmind-Agenten aufruft:
import {
  IExecuteFunctions,
  INodeExecutionData,
  INodeType,
  INodeTypeDescription,
  NodePropertyTypes,
} from 'n8n-workflow';

export class LocalmindAgent implements INodeType {
  description: INodeTypeDescription = {
    displayName: 'Localmind Agent',
    name: 'localmindAgent',
    icon: 'file:localmind.svg',
    group: ['transform'],
    version: 1,
    subtitle: '={{$parameter["operation"]}}',
    description: 'Ruft einen Localmind-Agenten auf',
    defaults: {
      name: 'Localmind Agent',
    },
    inputs: ['main'],
    outputs: ['main'],
    credentials: [
      {
        name: 'localmindApi',
        required: true,
      },
    ],
    properties: [
      {
        displayName: 'Agent ID',
        name: 'agentId',
        type: 'string',
        required: true,
        default: '',
        description: 'Die ID des Localmind-Agenten',
      },
      {
        displayName: 'Input',
        name: 'input',
        type: 'string',
        required: true,
        default: '',
        description: 'Der Input-Text für den Agenten',
      },
      {
        displayName: 'Temperature',
        name: 'temperature',
        type: 'number',
        typeOptions: {
          minValue: 0,
          maxValue: 2,
          numberStepSize: 0.1,
        },
        default: 0.7,
        description: 'Die Temperature für die Agent-Antwort',
      },
      {
        displayName: 'Max Tokens',
        name: 'maxTokens',
        type: 'number',
        typeOptions: {
          minValue: 1,
          maxValue: 4000,
        },
        default: 500,
        description: 'Maximale Anzahl von Tokens in der Antwort',
      },
    ],
  };

  async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
    const items = this.getInputData();
    const returnData: INodeExecutionData[] = [];

    for (let i = 0; i < items.length; i++) {
      const agentId = this.getNodeParameter('agentId', i) as string;
      const input = this.getNodeParameter('input', i) as string;
      const temperature = this.getNodeParameter('temperature', i) as number;
      const maxTokens = this.getNodeParameter('maxTokens', i) as number;

      const credentials = await this.getCredentials('localmindApi');
      const apiKey = credentials.apiKey as string;

      // API-Call zu Localmind
      const response = await this.helpers.httpRequest({
        method: 'POST',
        url: 'https://api.localmind.ai/v1/agents/execute',
        headers: {
          'Authorization': `Bearer ${apiKey}`,
          'Content-Type': 'application/json',
        },
        body: {
          agentId,
          input,
          options: {
            temperature,
            maxTokens,
          },
        },
      });

      returnData.push({
        json: {
          agentId,
          input,
          response: response.output,
          usage: response.usage,
        },
      });
    }

    return [returnData];
  }
}

Node-Konfiguration

Properties definieren

Properties sind die konfigurierbaren Parameter Ihres Nodes:
Text-Eingaben:
{
  displayName: 'Agent ID',
  name: 'agentId',
  type: 'string',
  required: true,
  default: '',
  description: 'Die ID des Localmind-Agenten',
}
Optionen:
  • required: Ob das Feld erforderlich ist
  • default: Standardwert
  • placeholder: Platzhalter-Text
Zahlen-Eingaben:
{
  displayName: 'Temperature',
  name: 'temperature',
  type: 'number',
  typeOptions: {
    minValue: 0,
    maxValue: 2,
    numberStepSize: 0.1,
  },
  default: 0.7,
}
Dropdown-Auswahl:
{
  displayName: 'Operation',
  name: 'operation',
  type: 'options',
  options: [
    {
      name: 'Analyze',
      value: 'analyze',
    },
    {
      name: 'Transform',
      value: 'transform',
    },
  ],
  default: 'analyze',
}
Komplexe Objekte:
{
  displayName: 'Parameters',
  name: 'parameters',
  type: 'collection',
  placeholder: 'Add Parameter',
  default: {},
  options: [
    {
      displayName: 'Key',
      name: 'key',
      type: 'string',
    },
    {
      displayName: 'Value',
      name: 'value',
      type: 'string',
    },
  ],
}

Credentials konfigurieren

Für API-Authentifizierung definieren Sie Credentials:
import {
  ICredentialType,
  INodeProperties,
} from 'n8n-workflow';

export class LocalmindApi implements ICredentialType {
  name = 'localmindApi';
  displayName = 'Localmind API';
  properties: INodeProperties[] = [
    {
      displayName: 'API Key',
      name: 'apiKey',
      type: 'string',
      typeOptions: {
        password: true,
      },
      default: '',
      required: true,
    },
    {
      displayName: 'Base URL',
      name: 'baseUrl',
      type: 'string',
      default: 'https://api.localmind.ai',
      required: true,
    },
  ];
}

Node-Typen im Detail

Regular Node

Ein Standard-Node, der Daten verarbeitet:
export class MyCustomNode implements INodeType {
  description: INodeTypeDescription = {
    displayName: 'My Custom Node',
    name: 'myCustomNode',
    group: ['transform'],
    version: 1,
    description: 'Beschreibung des Nodes',
    defaults: {
      name: 'My Custom Node',
    },
    inputs: ['main'],  // Anzahl der Inputs
    outputs: ['main'], // Anzahl der Outputs
    properties: [
      // Node Properties hier
    ],
  };

  async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
    // Node-Logik hier
    return [returnData];
  }
}

Trigger Node

Ein Node, der Workflows startet:
export class MyTriggerNode implements INodeType {
  description: INodeTypeDescription = {
    displayName: 'My Trigger',
    name: 'myTrigger',
    group: ['trigger'],
    version: 1,
    description: 'Startet Workflow bei Event',
    defaults: {
      name: 'My Trigger',
    },
    inputs: [],
    outputs: ['main'],
    properties: [
      // Trigger-spezifische Properties
    ],
  };

  async trigger(this: ITriggerFunctions): Promise<ITriggerResponse> {
    // Trigger-Logik hier
    return {
      close: async () => {
        // Cleanup
      },
      main: async () => {
        // Event-Handling
      },
    };
  }
}

Webhook Node

Ein Node, der HTTP-Requests empfängt:
export class MyWebhookNode implements INodeType {
  description: INodeTypeDescription = {
    displayName: 'My Webhook',
    name: 'myWebhook',
    group: ['trigger'],
    version: 1,
    description: 'Empfängt HTTP-Requests',
    defaults: {
      name: 'My Webhook',
    },
    inputs: [],
    outputs: ['main'],
    webhook: {
      httpMethod: 'POST',
      path: 'my-webhook',
    },
    properties: [
      // Webhook-spezifische Properties
    ],
  };

  async webhook(this: IWebhookFunctions): Promise<IWebhookResponseData> {
    const req = this.getRequestObject();
    const res = this.getResponseObject();

    return {
      workflowData: [
        [
          {
            json: req.body,
          },
        ],
      ],
    };
  }
}

Datenverarbeitung

Input-Daten lesen

async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
  const items = this.getInputData();
  const returnData: INodeExecutionData[] = [];

  for (let i = 0; i < items.length; i++) {
    // Zugriff auf einzelnes Item
    const item = items[i];
    const data = item.json;

    // Parameter aus Node-Konfiguration lesen
    const agentId = this.getNodeParameter('agentId', i) as string;
    
    // Verarbeitung...
  }

  return [returnData];
}

Output-Daten erstellen

// Einfaches Output
returnData.push({
  json: {
    result: 'success',
    data: processedData,
  },
});

// Mit Binary-Daten
returnData.push({
  json: {
    filename: 'result.json',
  },
  binary: {
    data: {
      data: Buffer.from(JSON.stringify(data)),
      mimeType: 'application/json',
    },
  },
});

Error Handling

Fehlerbehandlung implementieren

async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
  const items = this.getInputData();
  const returnData: INodeExecutionData[] = [];

  for (let i = 0; i < items.length; i++) {
    try {
      // Node-Logik
      const result = await processData();
      
      returnData.push({
        json: result,
      });
    } catch (error) {
      // Fehlerbehandlung
      if (this.continueOnFail()) {
        returnData.push({
          json: {
            error: error.message,
          },
        });
        continue;
      }
      
      // Fehler werfen, wenn continueOnFail nicht aktiviert
      throw error;
    }
  }

  return [returnData];
}

Testing

Node testen

1

Lokales Testing

  1. Kompilieren Sie Ihren Node:
npm run build
  1. Testen Sie den Node lokal mit Automate Dev-Modus
2

Unit Tests

Erstellen Sie Unit Tests für Ihre Node-Logik:
import { LocalmindAgent } from '../nodes/LocalmindAgent/LocalmindAgent.node';

describe('LocalmindAgent', () => {
  it('should execute successfully', async () => {
    // Test-Logik hier
  });
});
3

Integration Tests

Testen Sie den Node in echten Workflows:
  • Erstellen Sie Test-Workflows
  • Verwenden Sie Test-Daten
  • Überprüfen Sie Outputs

Deployment

Node verpacken

1

Build erstellen

Kompilieren Sie TypeScript zu JavaScript:
npm run build
2

Package erstellen

Erstellen Sie ein npm-Package:
npm pack
Dies erstellt eine .tgz Datei, die installiert werden kann.
3

Node installieren

Installieren Sie den Node in Automate:
# In Automate-Verzeichnis
npm install /path/to/localmind-custom-node-1.0.0.tgz

Node verteilen

Private npm Registry

Veröffentlichen Sie in einer privaten npm Registry für Ihr Team.

Git Repository

Verteilen Sie über Git und installieren direkt aus dem Repository.

Lokale Installation

Installieren Sie lokal für Entwicklung und Testing.

Docker Image

Packen Sie in Docker für Container-Deployments.

Best Practices

Klare Dokumentation

Dokumentieren Sie jeden Property und jede Funktion klar und verständlich.

Error Handling

Implementieren Sie umfassendes Error Handling für alle Fehlerfälle.

Performance

Optimieren Sie für Performance - vermeiden Sie unnötige API-Calls.

Wiederverwendbarkeit

Designen Sie Nodes für Wiederverwendbarkeit in verschiedenen Kontexten.

Testing

Schreiben Sie umfassende Tests für Ihre Nodes.

Versionierung

Verwenden Sie semantische Versionierung für Ihre Nodes.

Beispiel: Kompletter Custom Node

Hier ist ein vollständiges Beispiel eines Custom Nodes:
import {
  IExecuteFunctions,
  INodeExecutionData,
  INodeType,
  INodeTypeDescription,
} from 'n8n-workflow';

export class DataTransform implements INodeType {
  description: INodeTypeDescription = {
    displayName: 'Data Transform',
    name: 'dataTransform',
    icon: 'file:transform.svg',
    group: ['transform'],
    version: 1,
    description: 'Transformiert Daten nach definierten Regeln',
    defaults: {
      name: 'Data Transform',
    },
    inputs: ['main'],
    outputs: ['main'],
    properties: [
      {
        displayName: 'Operation',
        name: 'operation',
        type: 'options',
        options: [
          {
            name: 'Uppercase',
            value: 'uppercase',
          },
          {
            name: 'Lowercase',
            value: 'lowercase',
          },
          {
            name: 'Trim',
            value: 'trim',
          },
        ],
        default: 'uppercase',
        description: 'Die Transformations-Operation',
      },
      {
        displayName: 'Field Name',
        name: 'fieldName',
        type: 'string',
        default: 'text',
        description: 'Das zu transformierende Feld',
      },
    ],
  };

  async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
    const items = this.getInputData();
    const returnData: INodeExecutionData[] = [];

    for (let i = 0; i < items.length; i++) {
      const operation = this.getNodeParameter('operation', i) as string;
      const fieldName = this.getNodeParameter('fieldName', i) as string;
      const item = items[i];
      const value = item.json[fieldName] as string;

      let transformedValue: string;

      switch (operation) {
        case 'uppercase':
          transformedValue = value.toUpperCase();
          break;
        case 'lowercase':
          transformedValue = value.toLowerCase();
          break;
        case 'trim':
          transformedValue = value.trim();
          break;
        default:
          transformedValue = value;
      }

      returnData.push({
        json: {
          ...item.json,
          [fieldName]: transformedValue,
        },
      });
    }

    return [returnData];
  }
}

Integration mit Localmind

Localmind-spezifische Features

Agenten direkt aufrufen:Custom Nodes können Localmind-Agenten direkt integrieren:
const agentResponse = await callLocalmindAgent({
  agentId: 'agent_123',
  input: data.text,
  context: data.context,
});
Auf Localmind-Datenbanken zugreifen:Nodes können auf Ihre Localmind-Datenbanken zugreifen:
const result = await queryLocalmindDatabase({
  query: 'SELECT * FROM users WHERE id = ?',
  params: [userId],
});

Häufige Probleme

Problem: Custom Node erscheint nicht in der Node-ListeLösung:
  • Überprüfen Sie package.json Konfiguration
  • Stellen Sie sicher, dass der Node kompiliert wurde
  • Neustarten Sie Automate nach Installation
Problem: TypeScript-Kompilierung schlägt fehlLösung:
  • Überprüfen Sie tsconfig.json Konfiguration
  • Stellen Sie sicher, dass alle Dependencies installiert sind
  • Überprüfen Sie Type-Definitionen
Problem: Credentials werden nicht korrekt geladenLösung:
  • Überprüfen Sie Credential-Definition
  • Stellen Sie sicher, dass Credentials korrekt gespeichert sind
  • Testen Sie Credential-Zugriff im Node

Ressourcen


Brauchen Sie Hilfe? Unser Entwickler-Team unterstützt Sie gerne bei der Erstellung von Custom Nodes. Kontaktieren Sie uns unter dev@localmind.ai.