Irgendwann kommt in jedem n8n-Workflow der Moment, in dem die Standard-Nodes nicht mehr reichen. Du willst Daten gruppieren, ein verschachteltes Array auflösen oder eine KI-Antwort in sauberes JSON verwandeln, und plötzlich brauchst Du fünf Nodes für etwas, das in zehn Zeilen Code erledigt wäre. Genau dafür gibt es den n8n Code Node.
In diesem Beitrag zeige ich Dir zuerst kurz, wie der Code Node funktioniert, und dann 10 praktische Beispiele, die ich selbst immer wieder in meinen Workflows einsetze. Du kannst jedes Snippet direkt kopieren und an Deine Daten anpassen.
So funktioniert der n8n Code Node
Der Code Node bekommt eine Liste von Items als Input und muss wieder eine Liste von Items zurückgeben. Jedes Item ist ein Objekt mit einem json-Schlüssel, in dem Deine eigentlichen Daten stecken. Du kannst JavaScript oder Python schreiben. Ich nutze in den Beispielen JavaScript, weil Du dort den vollen Zugriff auf die eingebauten Helfer von n8n hast.
Wichtig ist die Wahl des Modus:
- Run Once for All Items: Dein Code läuft genau einmal und sieht alle Items gleichzeitig über
$input.all(). Ideal zum Filtern, Gruppieren, Sortieren und Zusammenfassen. - Run Once for Each Item: Dein Code läuft pro Item einmal. Das aktuelle Item erreichst Du über
$jsonbzw.$input.item. Ideal für Umrechnungen, die nur ein einzelnes Item betreffen.
Mein Tipp: Wenn Du unsicher bist, nimm „Run Once for All Items”. Damit kannst Du alles machen, was auch der andere Modus kann, und hast zusätzlich den Überblick über alle Daten. Außer bei Beispiel 3 nutze ich in diesem Beitrag immer diesen Modus.
10 praktische Beispiele für den n8n Code Node
1. Felder umbenennen und Daten neu strukturieren
Der Klassiker: Ein Webformular oder eine API liefert Felder wie first_name, Dein CRM erwartet aber vorname. Mit map() baust Du jedes Item in einem Rutsch um und bereinigst die Werte gleich mit.
|
1 2 3 4 5 6 7 8 9 10 11 |
return $input.all().map(item => { const d = item.json; return { json: { vorname: d.first_name?.trim(), nachname: d.last_name?.trim(), email: d.email?.toLowerCase().trim(), quelle: 'Webformular' } }; }); |
Für einfache Umbenennungen reicht oft auch der Edit Fields Node. Sobald Du aber Logik brauchst, etwa Trimmen, Kleinschreibung oder Fallbacks, ist der Code Node übersichtlicher.
2. Duplikate entfernen
Wenn Du Daten aus mehreren Quellen zusammenführst, landen schnell doppelte Kontakte im Workflow. Mit einem Set behältst Du nur den ersten Eintrag pro E-Mail-Adresse und wirfst leere Einträge gleich mit raus.
|
1 2 3 4 5 6 7 8 9 10 11 |
const gesehen = new Set(); const ergebnis = []; for (const item of $input.all()) { const email = (item.json.email || '').toLowerCase().trim(); if (!email || gesehen.has(email)) continue; gesehen.add(email); ergebnis.push({ json: { ...item.json, email } }); } return ergebnis; |
3. Datum formatieren mit Luxon
APIs liefern Zeitstempel meist im ISO-Format und in UTC. Für E-Mails oder Berichte brauchst Du aber deutsche Schreibweise und deutsche Zeitzone. Im Code Node steht Dir die Bibliothek Luxon über DateTime und $now direkt zur Verfügung. Dieses Beispiel läuft im Modus Run Once for Each Item.
|
1 2 3 4 5 6 7 8 9 10 11 12 |
const dt = DateTime.fromISO($json.created_at, { zone: 'utc' }) .setZone('Europe/Berlin'); return { json: { ...$json, datum_de: dt.toFormat('dd.MM.yyyy HH:mm'), kalenderwoche: dt.weekNumber, ist_wochenende: dt.weekday >= 6, tage_her: Math.floor($now.diff(dt, 'days').days) } }; |
4. Eingaben validieren
Bevor Daten in Dein CRM oder Deine Datenbank wandern, solltest Du sie prüfen. Ich markiere jedes Item mit gueltig und einer Fehlerliste. Danach entscheidet ein IF Node, ob das Item weiterläuft oder in einer Fehler-Benachrichtigung landet.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
const pflicht = ['name', 'email', 'plz']; return $input.all().map(item => { const d = item.json; const fehler = pflicht.filter(f => !d[f]).map(f => `${f} fehlt`); if (d.email && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(d.email)) { fehler.push('E-Mail ungültig'); } if (d.plz && !/^\d{5}$/.test(String(d.plz))) { fehler.push('PLZ ungültig'); } return { json: { ...d, gueltig: fehler.length === 0, fehler } }; }); |
5. Daten gruppieren und summieren
Du hast eine Liste von Bestellungen und willst wissen, welcher Kunde wie viel Umsatz gemacht hat? Das ist mit Standard-Nodes mühsam, im Code Node sind es ein paar Zeilen. Das Ergebnis ist direkt nach Umsatz sortiert.
|
1 2 3 4 5 6 7 8 9 10 11 |
const summen = {}; for (const { json } of $input.all()) { summen[json.kunde] ??= { kunde: json.kunde, bestellungen: 0, umsatz: 0 }; summen[json.kunde].bestellungen += 1; summen[json.kunde].umsatz += Number(json.betrag) || 0; } return Object.values(summen) .map(s => ({ json: { ...s, umsatz: Math.round(s.umsatz * 100) / 100 } })) .sort((a, b) => b.json.umsatz - a.json.umsatz); |
6. Verschachtelte Arrays auflösen
Shop-Systeme liefern eine Bestellung mit allen Positionen als Array. Für eine Tabelle oder Warenwirtschaft brauchst Du aber ein Item pro Position. Der Split Out Node kann das auch, im Code Node nimmst Du aber gleich die Bestellnummer und den Kunden mit in jede Zeile.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
const positionen = []; for (const { json: bestellung } of $input.all()) { for (const pos of bestellung.line_items || []) { positionen.push({ json: { bestellnummer: bestellung.id, kunde: bestellung.customer?.email, artikel: pos.sku, menge: pos.quantity, preis: pos.price } }); } } return positionen; |
7. Viele Items zu einer Nachricht zusammenfassen
Der umgekehrte Fall: Du hast viele Items, willst aber nur eine Slack-Nachricht oder eine E-Mail verschicken. Gibst Du einfach alle Items weiter, bekommst Du zehn Nachrichten statt einer. Deshalb fasse ich sie zu einem einzigen Item zusammen. Perfekt in Kombination mit Beispiel 5.
|
1 2 3 4 5 6 7 8 9 10 |
const zeilen = $input.all().map(({ json }) => `• ${json.kunde}: ${json.umsatz.toFixed(2)} €` ); return [{ json: { anzahl: zeilen.length, text: `Top-Kunden diese Woche:\n${zeilen.join('\n')}` } }]; |
8. Daten aus einem anderen Node nachschlagen
Mit $('Node-Name') greifst Du auf die Ausgabe jedes vorherigen Nodes zu, nicht nur auf den direkten Vorgänger. So kannst Du zum Beispiel Bestellungen mit Kundendaten aus einem früheren CRM-Abruf anreichern. Die Map sorgt dafür, dass das auch bei tausenden Datensätzen schnell bleibt.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
const kunden = $('CRM Kunden').all(); const index = new Map(kunden.map(k => [k.json.email, k.json])); return $input.all().map(item => { const kunde = index.get(item.json.email); return { json: { ...item.json, kundennummer: kunde?.id ?? null, segment: kunde?.segment ?? 'unbekannt' } }; }); |
Achte darauf, dass der Name in $('...') exakt dem Namen des Nodes in Deinem Workflow entspricht. Benennst Du den Node später um, musst Du den Code anpassen.
9. Eindeutige IDs per Hash erzeugen
Wenn Du verhindern willst, dass ein Datensatz doppelt verarbeitet wird, brauchst Du eine stabile ID. Ein SHA-256-Hash aus E-Mail und Bestellnummer ist dafür ideal: gleiche Eingabe, gleiche ID. Das Modul crypto ist in n8n Cloud freigegeben. Selbst gehostet musst Du es über die Umgebungsvariable NODE_FUNCTION_ALLOW_BUILTIN=crypto erlauben.
|
1 2 3 4 5 6 7 |
const crypto = require('crypto'); return $input.all().map(item => { const schluessel = `${item.json.email}|${item.json.bestellnummer}`; const hash = crypto.createHash('sha256').update(schluessel).digest('hex'); return { json: { ...item.json, dedupe_id: hash.slice(0, 16) } }; }); |
10. JSON aus einer KI-Antwort extrahieren
Wenn Du mit dem AI Agent oder einem LLM-Node arbeitest, kennst Du das Problem: Du bittest um reines JSON und bekommst trotzdem einen Satz davor oder einen Markdown-Codeblock drumherum. Dieses Snippet fischt das JSON-Objekt aus dem Text und fängt Fehler sauber ab, statt den ganzen Workflow abstürzen zu lassen.
|
1 2 3 4 5 6 7 8 9 10 11 |
return $input.all().map(item => { const roh = item.json.output || item.json.text || ''; const treffer = roh.match(/\{[\s\S]*\}/); try { const daten = JSON.parse(treffer ? treffer[0] : roh); return { json: { ...daten, parse_ok: true } }; } catch (e) { return { json: { parse_ok: false, fehler: e.message, rohtext: roh } }; } }); |
Mit einem IF Node auf parse_ok kannst Du fehlerhafte Antworten danach erneut an das Modell schicken oder Dich benachrichtigen lassen.
Welche JS Libs stehen Dir im Code Node zur Verfügung?
In den Beispielen hast Du schon Luxon und crypto gesehen. Welche Bibliotheken Du darüber hinaus nutzen kannst, hängt davon ab, ob Du n8n Cloud verwendest oder n8n selbst hostest. Ich unterscheide drei Stufen:
Immer verfügbar: ohne require()
Alles, was modernes JavaScript mitbringt, kannst Du direkt nutzen: JSON, Math, Map, Set, reguläre Ausdrücke oder Intl.NumberFormat für Währungen. Dazu kommen zwei Bibliotheken, die n8n fest eingebaut hat:
- Luxon für Datum und Uhrzeit über
DateTime,DurationundInterval.$nowund$todaysind bereits Luxon-Objekte (siehe Beispiel 3). - JMESPath über
$jmespath(objekt, 'abfrage'), um Werte aus tief verschachteltem JSON herauszuziehen.
|
1 2 3 4 5 6 7 8 |
const daten = $input.first().json; const skus = $jmespath(daten, 'line_items[?quantity > `1`].sku'); const betrag = new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(daten.total); return [{ json: { skus, betrag } }]; |
n8n Cloud: crypto und moment
In n8n Cloud kannst Du keine eigenen npm-Pakete nachladen. Freigegeben sind nur das Node.js-Modul crypto und das npm-Paket moment, die Du per require() einbindest. Für neue Workflows rate ich Dir zu Luxon statt moment, denn moment wird nicht mehr weiterentwickelt und Luxon ist ohnehin schon da.
Selbst gehostet: Du entscheidest
Auf Deinem eigenen Server blockiert n8n Imports standardmäßig. Freischalten kannst Du sie über zwei Umgebungsvariablen:
|
1 2 3 4 5 6 |
# Eingebaute Node.js-Module NODE_FUNCTION_ALLOW_BUILTIN=crypto,fs # Alle eingebauten Module NODE_FUNCTION_ALLOW_BUILTIN=* # Externe npm-Pakete NODE_FUNCTION_ALLOW_EXTERNAL=lodash,axios |
Bei externen Paketen reicht die Freigabe allein nicht: Das Paket muss auch im node_modules-Verzeichnis Deiner n8n-Installation liegen. Bei Docker baust Du dafür meist ein eigenes Image, in dem Du das Paket per npm install nachinstallierst. Danach nutzt Du es ganz normal:
|
1 2 3 4 5 6 |
const _ = require('lodash'); const gruppen = _.groupBy($input.all().map(i => i.json), 'kunde'); return Object.entries(gruppen).map(([kunde, liste]) => ({ json: { kunde, anzahl: liste.length } })); |
Ein typischer Stolperstein: Läuft Deine Instanz mit Task Runners, wird der Code nicht im Haupt-Prozess ausgeführt. Dann musst Du die Umgebungsvariablen beim Task Runner setzen und die Pakete auch dort installieren, sonst bekommst Du weiter die Fehlermeldung, dass das Modul nicht erlaubt ist. Details findest Du in der Anleitung Enable modules in Code node.
Gib außerdem nicht leichtfertig * oder Module wie fs und child_process frei. Jeder, der Workflows bearbeiten darf, kann damit auf Dateien und Prozesse Deines Servers zugreifen.
Code Node oder Standard-Node?
Nicht alles gehört in einen Code Node. Standard-Nodes sind für andere leichter zu lesen und brauchen keine Programmierkenntnisse. Ich halte mich an eine einfache Faustregel:
Wenn Du merkst, dass Du drei oder mehr Nodes hintereinander schaltest, nur um Daten umzuformen, ist das ein gutes Signal für einen Code Node.
Wie debugge ich eine Code Node?
Früher oder später macht Dein Code nicht das, was er soll: Der Node wirft einen Fehler, liefert leere Items oder rechnet mit den falschen Werten. Statt auf gut Glück herumzuprobieren, gehe ich beim Debuggen immer in derselben Reihenfolge vor.
Schritt 1: Eingangsdaten pinnen
Bevor ich überhaupt den Code anfasse, pinne ich die Ausgabe des vorherigen Nodes. Dafür klickst Du im Output-Panel des Vorgänger-Nodes auf das Pin-Symbol. Ab dann arbeitet der Code Node bei jedem Testlauf mit genau diesen Daten, ohne dass Webhooks, APIs oder KI-Modelle erneut aufgerufen werden. Das spart Zeit, Kosten und sorgt dafür, dass Du einen Fehler zuverlässig reproduzieren kannst.
Schritt 2: Den Node einzeln ausführen
Öffne den Code Node und klicke auf Execute step (in älteren Versionen „Test step”). So läuft nur dieser eine Node und Du siehst links den Input und rechts den Output direkt nebeneinander. Prüfe zuerst, ob der Input überhaupt so aussieht, wie Dein Code ihn erwartet. Ein großer Teil aller Fehler liegt nicht im Code, sondern in Feldnamen, die anders heißen oder tiefer verschachtelt sind als gedacht.
Schritt 3: Die Fehlermeldung genau lesen
Wirft der Code einen Fehler, zeigt n8n die Meldung samt Zeilennummer an, etwa Cannot read properties of undefined (reading 'email') [line 3]. Das bedeutet fast immer: Das Objekt vor .email existiert nicht. Die häufigsten Ursachen, die ich sehe:
- Falscher Modus:
$jsonfunktioniert nur in „Run Once for Each Item”. In „Run Once for All Items” arbeitest Du mit$input.all(). - Fehlende Felder: Ein einzelnes Item hat das Feld nicht. Optional Chaining wie
d.customer?.emailfängt das ab. - Falsches Rückgabeformat: Du gibst ein Objekt statt eines Arrays zurück oder vergisst den
json-Schlüssel. - Falscher Node-Name:
$('CRM Kunden')passt nicht mehr, weil der Node umbenannt wurde.
Schritt 4: Werte mit console.log() prüfen
Mit console.log() schaust Du Deinem Code beim Arbeiten zu. Die Ausgabe landet in der Konsole Deines Browsers, nicht in n8n selbst. Öffne also die Entwicklertools (F12 bzw. Cmd + Option + I auf dem Mac), wechsle zum Tab „Konsole” und führe den Node aus. Objekte gebe ich mit JSON.stringify() aus, dann sind sie auch bei tiefer Verschachtelung gut lesbar.
|
1 2 3 4 5 6 7 8 9 |
const items = $input.all(); console.log('Anzahl Items:', items.length); console.log('Erstes Item:', JSON.stringify(items[0]?.json, null, 2)); for (const [i, item] of items.entries()) { if (!item.json.email) console.log(`Item ${i} hat keine E-Mail`, item.json); } return items; |
In Python nutzt Du statt console.log() einfach print(). Wichtig: Die Ausgabe erscheint nur, wenn Du den Workflow manuell im Editor ausführst. Bei produktiven Ausführungen, etwa über einen Webhook oder Cron-Trigger, siehst Du davon im Browser nichts. Bei einer selbst gehosteten Instanz kannst Du die Logs über die Umgebungsvariable CODE_ENABLE_STDOUT=true zusätzlich in die Server-Logs schreiben lassen.
Schritt 5: Zwischenstände als Debug-Item zurückgeben
Oft noch schneller als die Browser-Konsole: Ich gebe vorübergehend einfach das zurück, was mich interessiert. Das Ergebnis sehe ich sofort im Output-Panel, wahlweise als Tabelle oder JSON.
|
1 2 3 4 5 6 7 8 9 10 |
const items = $input.all(); return [{ json: { anzahl: items.length, felder: Object.keys(items[0]?.json ?? {}), ohne_email: items.filter(i => !i.json.email).length, beispiel: items[0]?.json } }]; |
Wenn Du weißt, welche Bedingung niemals eintreten darf, kannst Du auch gezielt einen eigenen Fehler werfen. Der Workflow stoppt dann genau an dieser Stelle mit einer Meldung, die Du sofort verstehst:
|
1 2 3 4 5 6 7 |
for (const [i, item] of $input.all().entries()) { if (typeof item.json.betrag !== 'number') { throw new Error(`Item ${i}: "betrag" ist keine Zahl, sondern ${JSON.stringify(item.json.betrag)}`); } } return $input.all(); |
Fehler aus produktiven Ausführungen nachstellen
Ist ein Workflow im Live-Betrieb fehlgeschlagen, öffnest Du im Tab Executions die betroffene Ausführung. Dort siehst Du für jeden Node, mit welchen Daten er gelaufen ist und wo der Fehler aufgetreten ist. Mit Debug in editor kopiert n8n die Daten dieser Ausführung in Deinen Workflow und pinnt sie an den ersten Node. Danach debuggst Du wie oben beschrieben mit genau den Daten, die den Fehler ausgelöst haben. Die Funktion gibt es in allen Cloud-Plänen und bei selbst gehosteten Instanzen ab der registrierten Community Edition. Mehr dazu findest Du in der n8n-Dokumentation unter Debug and re-run past executions.
Meine Tipps für saubere Code Nodes
- Benenne den Node sprechend. „Umsatz pro Kunde summieren” ist besser als „Code3″.
- Gib immer ein Array zurück. Im Modus „Run Once for All Items” erwartet n8n eine Liste von Items, auch wenn es nur eines ist.
- Nutze
console.log()zum Debuggen. Die Ausgabe erscheint in der Browser-Konsole, wenn Du den Node manuell ausführst. - Fange Fehler ab. Ein
try/catchwie in Beispiel 10 verhindert, dass ein einziges kaputtes Item den ganzen Workflow stoppt. - Lass Dir von der KI helfen. Der eingebaute KI-Assistent oder ChatGPT schreiben Dir brauchbare Snippets, wenn Du ein Beispiel-Item als JSON mitgibst.
Alle eingebauten Variablen und Methoden findest Du in der offiziellen n8n-Dokumentation zum Code Node und in der Übersicht der Built-in Methods and Variables.
Häufige Fragen zum n8n Code Node
Der Code Node unterstützt JavaScript und Python. JavaScript ist der Standard und bietet Dir den vollen Zugriff auf die eingebauten Helfer wie Luxon, Im ersten Modus läuft Dein Code einmal und sieht alle Items über Ohne Import nutzt Du immer den JavaScript-Standard sowie Luxon ( Bei einer selbst gehosteten Instanz ja. Externe Pakete gibst Du über Meist gibst Du ein einzelnes Objekt statt eines Arrays zurück oder vergisst den Ja, über Pinne zuerst die Ausgabe des vorherigen Nodes und führe den Code Node dann einzeln über „Execute step” aus. Lies die Fehlermeldung samt Zeilennummer, prüfe Werte mit Die Ausgabe erscheint nicht in n8n, sondern in der Entwicklerkonsole Deines Browsers, und zwar nur bei manuellen Ausführungen im Editor. Bei selbst gehosteten Instanzen kannst Du die Logs mit
Welche Programmiersprachen unterstützt der n8n Code Node?
$input oder $('Node-Name'). Python eignet sich vor allem, wenn Du bereits Python-Code hast oder Dich darin wohler fühlst.Was ist der Unterschied zwischen „Run Once for All Items” und „Run Once for Each Item”?
$input.all(). Im zweiten Modus läuft er für jedes Item separat und Du greifst über $json auf das aktuelle Item zu. Für Gruppieren, Filtern und Zusammenfassen brauchst Du den ersten Modus.Welche JS Libs stehen mir im n8n Code Node zur Verfügung?
DateTime, Duration, Interval) und JMESPath ($jmespath). In n8n Cloud kommen per require() nur crypto und moment dazu. Selbst gehostet kannst Du beliebige Node.js-Module und installierte npm-Pakete freischalten.Kann ich npm-Pakete im Code Node verwenden?
NODE_FUNCTION_ALLOW_EXTERNAL frei, eingebaute Node.js-Module wie crypto über NODE_FUNCTION_ALLOW_BUILTIN. In n8n Cloud sind nur wenige Module wie crypto und moment verfügbar.Warum bekomme ich den Fehler, dass Items im falschen Format zurückgegeben werden?
json-Schlüssel. Im Modus „Run Once for All Items” sollte Dein Rückgabewert immer so aussehen: [{ json: { ... } }].Kann ich im Code Node HTTP-Requests ausführen?
this.helpers.httpRequest() kannst Du direkt aus dem Code heraus APIs aufrufen. In den meisten Fällen ist der HTTP Request Node aber die bessere Wahl, weil er Credentials, Pagination und Fehlerbehandlung schon mitbringt.Wie debugge ich eine Code Node?
console.log() in der Browser-Konsole (F12) oder gib Zwischenstände als Debug-Item zurück. Fehlgeschlagene produktive Ausführungen holst Du über „Debug in editor” mit den Originaldaten in den Editor.Warum sehe ich keine Ausgabe von console.log()?
CODE_ENABLE_STDOUT=true zusätzlich in die Server-Logs schreiben lassen.
