Erster Workflow: manuell auslösen → Felder bearbeiten → Code
Das Thema Angebotserstellung habe ich zwei Jahre lang manuell in einer Tabelle berechnet: Menge mal Stückpreis, plus Versandkosten, dann zurückrechnen auf die Stückkosten. Zweihundert Beutel, Stückpreis 1,85, Versand 60, macht zusammen 430, pro Stück 2,15. Die Zahlen sind nicht kompliziert, das Problem war, dass ich zweimal die Versandkosten vergessen habe, als das Angebot beim Kunden schon raus war.
Später wanderte diese Rechnung in n8n, drei Nodes, eine Zahl ändern und nochmal laufen lassen, zwei Sekunden bis zum neuen Preis. Dieser Artikel beschreibt genau die drei Schritte von der leeren Leinwand bis zum lauffähigen Workflow, inklusive der Falle, bei der ich 430 zum ersten Mal als 37060 berechnet habe.
Voraussetzungen sind nur zwei Dinge: n8n ist bereits installiert und lässt sich öffnen (wer es nicht installiert hat, schaut in《n8n 怎么安装》), Leinwand, Node-Panel und Ausführungsprotokoll sind vertraut (wer nicht, blättert in《界面导览》). Man muss nicht programmieren können, den Code-Abschnitt kann man einfach komplett übernehmen.
- Drei Nodes, jede für eine Aufgabe
- Was genau zwischen den Nodes weitergegeben wird
- Von der leeren Leinwand zu drei Nodes
- Feld bearbeiten: einem Datensatz vier Felder hinzufügen
- Code-Node: einen nehmen, einmal rechnen, einen zurückgeben
- Einmal laufen lassen, Ausgabe ansehen
- Anhang: das komplette Workflow-JSON
- Woran man erkennt, dass es wirklich läuft
- Fünf Fehler, die wirklich auftreten
- Die drei Grenzen des Code-Nodes
Drei Nodes, jede für eine Aufgabe
Der häufigste Fehler von Einsteigern ist, die drei Nodes als „drei Schritte" zu behandeln. Sie sind keine Schritte, sondern drei Rollen.
- Manual Trigger (manuelle Auslösung): erzeugt einen leeren Datensatz und schickt ihn weiter. Er fragt keine Datenbank ab, empfängt keine Requests, prüft keine Zeit – er läuft schlicht „einmal pro Klick". Der erste Workflow nutzt ihn, um die Variablen auf eine zu reduzieren – die Frage, woher die Daten kommen, muss erst einmal nicht geklärt werden.
- Edit Fields (Felder bearbeiten): fügt Felder in ein item ein oder ändert bestehende. Die Anzahl bleibt gleich, rein kommt eins, raus geht eins. In alten Tutorials heißt er Set, umbenannt wurde er vor einer Weile, im Node-Panel von 2.43.7 steht Edit Fields (Set), beide Namen sind auffindbar.
- Code (Code): der einzige Schritt, der die Anzahl ändern kann. Ein Eintrag kann in mehrere aufgeteilt werden, mehrere können zu einem zusammengefasst werden. Deshalb muss sein Rückgabewert ein Array sein – das ist der größte Unterschied zu anderen Nodes und zugleich die Stelle mit den meisten Fehlern.
Meine Instanz ist 2.43.7, die Oberfläche auf vereinfachtes Chinesisch umgestellt. Eine Warnung: Die chinesische Oberfläche übersetzt nur Beschreibungstexte und Buttons, Node-Namen und Parameternamen bleiben Englisch. Wer nach „编辑字段" sucht, findet nichts, man muss nach edit fieldssuchen. Die Tutorials im Netz, die „Set-Node" und „Set-Node's Keep Only Set Fields" schreiben, passen genauso wenig zur 2.x-Oberfläche – zweifle nicht an dir selbst.
Was genau wird zwischen den Nodes übertragen
Ohne das zu klären, lässt sich jeder spätere Fehler nur erraten.
Alle Daten, die in n8n zwischen Nodes fließen, sind ein Array, und jedes Element im Array sieht so aus:
[
{ "json": { "qty": 200 } },
{ "json": { "qty": 150 } }
]
Die äußeren eckigen Klammern sind „ein Batch", und jedes Objekt darin ist ein Datensatz, den n8n als item bezeichnet. Die eigentlichen Geschäftsfelder stecken in json diesem Key. „200 Bestellungen verarbeiten" bedeutet in diesem Modell also nicht, 200-mal zu schleifen, sondern dem Node einmal 200 items zu geben, die der Node selbst nacheinander abarbeitet.
Dieses Design hat zwei Konsequenzen. Die gute Nachricht: Nodes arbeiten von Natur aus batchweise, du musst keine Schleife schreiben. Die schlechte Nachricht: Wenn du Code schreibst, hast du es mit einem Array zu tun, nicht mit einem einzelnen Objekt —row.qty Diese Schreibweise lässt sich nicht direkt auf ein Array anwenden, man muss zuerst das erste Element herausnehmen.
Von der leeren Leinwand zu drei Nodes
- Oben rechts in der Workflow-Liste „Neuer Workflow", oder direkt in die Adressleiste tippen
/workflow/new. Ich bevorzuge Letzteres, geht schneller. - Auf der leeren Leinwand oben rechts auf das +klicken (alternativ auf das Plus in der Mitte der Leinwand), das Panel zeigt „Was passiert als Nächstes?".
- Im Suchfeld
manual triggereingeben, Manual Trigger auswählen. - Mit der Maus auf den kleinen Kreis rechts am Node fahren, es erscheint ein +, darauf klicken, suchen nach
edit fields, auswählen Edit Fields (Set). Wenn du es auf diese Weise hinzufügst, werden die Verbindungen automatisch hergestellt, ohne dass du sie manuell ziehen musst. - Wiederhole es noch einmal, suche
code, wähle Code aus. - Der Knotenname ist standardmäßig auf Englisch. Durch Doppelklick auf den Titel kannst du ihn ändern; ich habe ihn auf Chinesisch geändert: Manuell auslösen, Angebotsformular ausfüllen, Betrag berechnen. Bei vielen Knoten sieht man beim Überfliegen der englischen Namen nur Trigger, Set, Code – die Änderung auf Chinesisch dient rein der Augenschonung, funktional hat es keinerlei Auswirkung.

Felder bearbeiten: einem Datensatz vier Felder hinzufügen
Doppelklicke auf „Angebotsformular ausfüllen", um das Panel zu öffnen; nur zwei Parameter müssen geändert werden.
Mode beibehalten Manual Mapping, das ist die Formular-Eingabemethode. Die andere Option JSON Output bedeutet, direkt ein JSON-Stück zu schreiben, was bei verschachtelten Strukturen einfacher ist – in der Einstiegsphase noch nicht verwenden.
Fields to Set vier Zeilen hinzufügen. Jede Zeile hat links den Feldnamen, in der Mitte ein Typ-Dropdown, rechts den Wert:
| Feldname | Typ | 值 |
|---|---|---|
| product | Zeichenkette | Vakuum-Kompressionsbeutel 60x80cm |
| qty | Zahl | 200 |
| unit_price | Zahl | 1.85 |
| freight | Zahl | 60 |
Der Typ-Dropdown ist standardmäßig auf „Zeichenkette“ gesetzt; qty, unit_price und freight müssen manuell auf „Zahl“ geändert werden. Die Oberfläche hindert dich nicht daran, und bei falscher Auswahl gibt es keine Fehlermeldung – sie speichert 200 still und leise als "200". Wie schmerzhaft diese Falle ist, zeige ich dir gleich anhand der Berechnung.

row.unit_price– fehlt ein Zeichen, ist es undefined.Der Schalter ganz unten im Panel Include Other Input Fields ist standardmäßig aktiviert und bedeutet: „Felder in der Eingabe, die ich nicht angefasst habe, werden unverändert ausgegeben.“ Die Eingabe dieser Pipeline ist leer, daher macht es keinen Unterschied, ob der Schalter an oder aus ist. Bei einer Pipeline mit Daten jedoch bedeutet das Ausschalten, dass alle Upstream-Felder abgeschnitten werden und nur die vier manuell eingegebenen übrig bleiben – wenn dann im Code product nicht abgerufen werden kann, denk nicht, der Code sei falsch geschrieben.
Code node: take one, compute once, return one
Double-click "Calculate Amount".Mode Keep Run Once for All Items(run once for the entire array),Language Keep JavaScript. Then paste the following into the editor:
const row = items[0].json;
const amount = row.qty * row.unit_price + row.freight;
const unitCost = Number((amount / row.qty).toFixed(2));
return [{
json: {
product: row.product,
qty: row.qty,
amount,
unitCost,
summary: row.product + ' ' + row.qty + ' 个,含运费合计 ' + amount + ' 元,单个成本 ' + unitCost + ' 元',
},
}];
Line by line, only covering the parts that can bite you.
items[0].json is taking the first item. There is only one item here, so [0] is enough. For multi-item scenarios you need to write items.map(...), that is for the next article.items is the old syntax, still usable in 2.x; the new syntax is $input.first().json, both produce exactly the same result on this instance. I keep items, because in "Run Once for All Items" mode you already have the entire array in hand, and this name is more honest.
toFixed(2) This is not for looks. 430 divided by 200 is exactly 2.15, which looks clean; change it to shipping cost 55, and it becomes 2.175, and floating-point may produce 2.1749999999999998. Written into a table or sent to a customer, that kind of tail is ugly. The outer layer Number() Das wandelt es vom String zurück in eine Zahl –toFixed Zurückgegeben wird ein String; ohne Umwandlung wird unitCost in der Tabelle linksbündig ausgerichtet.
Der Abschnitt am Ende return ist der entscheidende Punkt.Gibt ein Array zurück, jedes Element wird in eine json-Schicht verpackt. n8n unterscheidet anhand dieses json-Schlüssels zwischen „das sind Geschäftsdaten" und „das ist ein binärer Anhang". Stimmt das Format nicht, zeigt der Node sofort rot.

Einmal ausführen, Ausgabe prüfen
Klicke unten auf der Canvas auf das orange „Ausführen". Nach ein bis zwei Sekunden werden die Ränder der drei Nodes grün, jeder Node erhält oben rechts ein kleines Häkchen, und auf den Verbindungslinien steht „1 Element".

Doppelklicke auf „Betrag berechnen", wechsle im rechten Ausgabebereich zum JSON-Tab:

Nebenbei ein leicht missverständlicher Punkt: Die Schaltfläche „Ausführen" läuft nur, wenn du sie anklickst. Ein Workflow mit Manual Trigger startet nicht automatisch, nur weil er veröffentlicht wurde – er hat keinen Mechanismus zum „Selbstlaufen". Für zeitgesteuerte Ausführung musst du auf einen Trigger-Typ wie Schedule Trigger wechseln.
Anhang: Das vollständige Workflow-JSON
Wenn du keine Lust hast, Nodes einzeln anzuklicken, kannst du das Ganze importieren. Kopiere den folgenden Abschnitt und drücke auf der leeren Canvas-Fläche Ctrl+V, dann landen die drei Nodes direkt auf dem Canvas. Über das Menü geht es auch: oben rechts ⋯ → Importieren → Aus Datei importieren, speichere den Inhalt zuerst als .json。
Diesen Abschnitt habe ich selbst in einen leeren Workflow eingefügt und einmal durchlaufen lassen: drei Nodes, zwei Verbindungen, Positionen stimmen, nach der Ausführung ist amount ebenfalls 430.
{
"name": "第一个工作流",
"nodes": [
{"parameters":{},"id":"m1","name":"手动触发","type":"n8n-nodes-base.manualTrigger","typeVersion":1,"position":[-368,0]},
{"parameters":{"assignments":{"assignments":[
{"id":"a1","name":"product","value":"真空压缩袋 60x80cm","type":"string"},
{"id":"a2","name":"qty","value":200,"type":"number"},
{"id":"a3","name":"unit_price","value":1.85,"type":"number"},
{"id":"a4","name":"freight","value":60,"type":"number"}
]},"includeOtherFields":true,"options":{}},"id":"m2","name":"填写报价单","type":"n8n-nodes-base.set","typeVersion":3.5,"position":[-144,0]},
{"parameters":{"jsCode":"const row = items[0].json;nconst amount = row.qty * row.unit_price + row.freight;nconst unitCost = Number((amount / row.qty).toFixed(2));nnreturn [{n json: {n product: row.product,n qty: row.qty,n amount,n unitCost,n summary: row.product + ' ' + row.qty + ' 个,含运费合计 ' + amount + ' 元,单个成本 ' + unitCost + ' 元',n },n}];","mode":"runOnceForAllItems"},"id":"m3","name":"计算金额","type":"n8n-nodes-base.code","typeVersion":2,"position":[80,0]}
],
"connections": {
"手动触发": {"main": [[{"node": "填写报价单", "type": "main", "index": 0}]]},
"填写报价单": {"main": [[{"node": "计算金额", "type": "main", "index": 0}]]}
},
"settings": {"executionOrder": "v1"}
}
Woran man erkennt, dass es wirklich durchgelaufen ist
Vier Punkte, alle selbst überprüfbar, fehlt einer, zählt es nicht als abgeschlossen.
- Drei Nodes mit grünem Rahmen und Haken, beide Verbindungen mit „1 Element" beschriftet. Steht dort nicht 1, hat ein Node zwischendurch die Anzahl geändert, dann nachprüfen.
- In der JSON-Ausgabe des Code-Nodes ist amount 430, unitCost 2.15.
- In der Ausführungshistorie findet sich dieser Lauf. Auch manuell gestartete Läufe werden protokolliert, nicht nur geplante Tasks landen in der Liste.
- Ändere freight von 60 auf 100 und starte erneut, dann sollte amount zu 470 werden. Einen Input ändern und sehen, dass der Output folgt – dieser Punkt ist härter als die drei davor: nur einmal 430 zu sehen, könnte Zufall sein.
Den vierten Punkt empfehle ich jedes Mal durchzuführen. Einmal durchgelaufen und richtig durchgelaufen liegen eine Schicht auseinander.
Fünf Fehler, die wirklich auftreten
1. Code doesn't return items properly
Ein String wurde zurückgegeben. In meinem Test habe ich nur eine Zeile geschrieben return "共 430 元";, der Node wird sofort rot, unten rechts erscheint diese Meldung.

Interessant ist,return { amount: 430 } diese Schreibweise ohne Array-Umhüllung, ohne json-Umhüllungläuft. Der Code-Node ergänzt automatisch den json-Schlüssel und die eckigen Klammern, dieses Verhalten ist in der Dokumentation beschrieben. Diese Fehlermeldung bedeutet also nicht „Format nicht standardkonform", sondern „was du zurückgibst, sind überhaupt keine Daten" — Strings, Zahlen, null lassen sich nicht ergänzen.
2. items is not defined [line 1]
Mode ist auf Run Once for Each Itemgesetzt, im Code wird aber weiterhin itemsverwendet. Im Einzelmodus füttert n8n jeweils nur einen Eintrag ein, der aktuelle heißt $json, für die Anzahl schreibt man $json.qty. Der Unterschied zwischen den beiden Modi in einem Satz: das ganze Array einmal durchlaufen oder jeden Eintrag einmal. Vor dem Codeschreiben kurz auf dieses Dropdown schauen.
3. In einem Workflow ist nur ein "Manual Trigger"-Node erlaubt
In einen Workflow, der bereits einen Trigger hat, noch einen Trigger hineinsetzen. Diesmal war mein Fehler noch dümmer: ein JSON mit Trigger in die Canvas eines bestehenden Workflows eingefügt, es erscheint „无法插入节点", darunter diese Meldung. Zum Vergleichstesten zuerst einen neuen Workflow öffnen und dann einfügen.
4. Gibt ein undefined aus, ohne Fehler zu melden
In Edit Fields heißt das Feld unit_price, im Code wird es geschrieben als row.unitPrice. Ein Zeichen Unterschied bei Groß-/Kleinschreibung und Unterstrich, n8n ignoriert das, JS auch, das Ergebnis ist, dass undefined durchgereicht wird und im summary „含运费合计 undefined 元" auftaucht. Solche Fehler sind am schwersten zu finden, weil sie nicht rot werden, nicht piepen, nur die Zahl stimmt nicht.
5. amount wird zu "37060"
Als Feldtyp wurde String gewählt. Sechzig und "60" sehen in JS gleich aus, aber + bei Strings wird stattdessen konkateniert: 370 plus "60" ergibt "37060", nicht 430.

Punkt fünf ist der wertvollste in diesem Artikel. Als ich diese Zahl zum ersten Mal herausbekam, starrte ich eine halbe Minute auf 37060, bis mir klar wurde, dass es String-Konkatenation ist – weil der Code völlig korrekt aussieht, das Problem liegt im Typ-Dropdown des vorherigen Nodes.
Die drei Grenzen des Code-Nodes
Der Code-Node läuft im n8n-eigenen Prozess, aber in einer Sandbox: Er kann keine lokalen Dateien lesen, keine HTTP-Requests senden. Für Dateizugriff den Read/Write File From Disk-Node verwenden, für Requests den HTTP Request-Node. Das ist keine Unvollständigkeit, sondern minimiert, „was ein schnell eingefügtes Stück JS anrichten kann".
Aber drei Dinge, die er nicht verhindern kann, muss man selbst im Griff haben.
Fremde Workflow-JSONs nicht einfach einfügen.Einfügen bedeutet, fremdes JS auf der eigenen Instanz auszuführen. Bei meiner Instanz landet eingefügtes JSON direkt in den Nodes, ohne Bestätigung, ohne Quellenabfrage. Zum Testen zuerst in eine leere Testinstanz werfen, den Code durchlesen, dann entscheiden, ob man ihn laufen lässt.
Keys nicht hart im Code kodieren.Workflows lassen sich als Ganzes exportieren, an Kollegen weitergeben und in Gruppen posten. Ein fest im Code hinterlegter api key wandert mit dem JSON mit. Er gehört in einen Credential-Node, damit n8n ihn verschlüsselt speichert.
Self-Hosting kann第三方 npm-Pakete installieren.Standardmäßig aus, muss manuell über eine Umgebungsvariable aktiviert werden. Aktiviert öffnet es dem Code-Node ein Tor zur beliebigen Codeausführung. Bei einer Instanz für eine Person egal, bei einer von mehreren Personen gemeinsam genutzten Instanz würde ich es dauerhaft aus lassen.
Kurzfassung
Drei Nodes, eine Kette: Der Trigger erzeugt Daten, Edit Fields ergänzt Daten, Code berechnet Daten. Was wirklich zu merken ist, ist nicht, wie man diese drei Nodes anklickt, sondern die Form „Array in json verschachtelt". Alles danach – IF, Merge, Schleifen, Fehlerzweige – sind Varianten dieser Form.
Code möglichst vermeiden. Dieses Angebot lässt sich mit Edit Fields plus Ausdrücken ebenfalls berechnen,{{ $json.qty * $json.unit_price + $json.freight }} eine Zeile erledigt es, und ein Node weniger. Ich habe mich am Ende für Code entschieden, weil später gleichzeitig Angebote in drei Varianten berechnet werden müssen und gestapelte Ausdrücke im Node schwerer zu pflegen sind als Code. Wenn du nur eine Zahl berechnest, dann verzichte auf Code.
Bei der Typ-Falle empfehle ich, freight jetzt absichtlich einmal in einen String zu ändern und laufen zu lassen, um mit eigenen Augen zu sehen, wie aus 430 plötzlich 37060 wird. Zehnmal Text lesen ist weniger wert, als einmal selbst einen Fehler zu machen.
Weiter geht es
Der vorherige Beitrag ist„n8n, Zapier, Make – wie wählen". Wie Nodes, Verbindungen und Ausführungen im JSON aussehen,dieser Beitraghat es zerlegt. Falls der Dienst irgendwann nicht startet oder Ports kollidieren, siehe„n8n-Startfehler und Portbelegung prüfen". Zur Installation siehediesen Artikel, bei unbekannter Oberfläche siehediesen Artikel. Das Inhaltsverzeichnis des gesamten Kurses befindet sichhier。
Referenzmaterial
- Manual Trigger: Wird nur ausgeführt, wenn du auf Ausführen klickst, bietet keine automatische Auslösung; pro Workflow ist nur einer erlaubt
- Edit Fields (Set): Zwei Modi Manual Mapping und JSON Output, die Funktion von Include in Output
- Code: Unterschiede der beiden Modi, Sprachoptionen, kein Zugriff auf das Dateisystem und keine HTTP-Anfragen möglich
- Understand n8n's data structure: Die Herkunft von Array-in-JSON und die Erklärung, dass der Code-Node automatisch JSON-Schlüssel ergänzt
- Integrierte Shortcuts verwenden: Integrierte Variablen und Methoden in Ausdrücken, welche im Code-Node nicht anwendbar sind
Letzte Aktualisierung: 28. Oktober 2026, verwendet wird n8n 2.43.7.

