Daten laden
Laden Sie die Rohdaten mit Node-RED kontrolliert aus einer konkreten Quelle in msg.payload. Die Beispiele zeigen denselben fachlichen Datensatz über HTTP, MQTT, S3 und PostgreSQL.
Diese Anleitung beginnt, nachdem Erreichbarkeit, Ressource und – bei einer geschützten Quelle – der technische Lesezugang unter Datenquelle prüfen nachgewiesen wurden. Sie endet bewusst vor dem fachlichen Mapping und dem Schreiben an ein Ziel.
Ladeweg auswählen
Wählen Sie denselben Quellenweg wie im vorherigen Schritt. Jeder Tab zeigt, welche Node benötigt wird, wie die Quelldaten geladen werden und woran Sie ein verwendbares Ergebnis in msg.payload erkennen.
- HTTP-API
- MQTT
- Datei / S3
- Datenbank
Die Standard-Node http request ruft den bereits geprüften Endpunkt ab. Das Beispiel übernimmt das items-Array aus der Antwort:
Inject → Function „Abruf vorbereiten“ → http request → Function „Antwort prüfen“
1. Abruf vorbereiten
Setzen Sie Methode, URL und fachliche Parameter in einer Function Node. Das Beispiel lädt einen kleinen, reproduzierbaren Ausschnitt:
msg.method = "GET";
msg.url = "https://data.beispiel-organisation.de/v1/traffic-counts"
+ "?updated_since=2026-08-19T07%3A55%3A00Z&limit=100";
msg.headers = {Accept: "application/json"};
return msg;
Konfigurieren Sie http request mit Methode aus msg.method, einer leeren Node-URL für die URL aus msg.url und der Rückgabe als parsed JSON object.
Die Administration stellt für geschützte APIs den technischen Client und den abgesicherten Secret- oder
Tokenweg bereit. Speichern Sie Client-Secret, Passwort oder langlebiges Token weder in einer Function Node
noch in msg. Verwenden Sie auch die Credentialfelder der HTTP-Node erst, wenn deren Schutz für
die konkrete Node-RED-Instanz geprüft wurde. Kurzlebige Access Tokens dürfen nur im vorgesehenen
Authentifizierungspfad verarbeitet und niemals im Debug-Fenster ausgegeben werden.
2. Status und Antwort prüfen
Die Function Node hinter http request akzeptiert nur die erwartete Antwort und legt das fachliche Array in msg.payload ab:
if (msg.statusCode !== 200) {
throw new Error(`API-Abruf fehlgeschlagen: HTTP ${msg.statusCode}`);
}
const rows = msg.payload?.items;
if (!Array.isArray(rows)) {
throw new Error("API-Antwort enthält kein items-Array");
}
msg.payload = rows;
msg.source = {type: "http", receivedAt: new Date().toISOString()};
return msg;
Soweit die API diese Mechanismen verwendet, muss der produktive Flow außerdem Pagination, Rate Limits und 429 Too Many Requests behandeln. Wiederholen Sie Fehler nur begrenzt und schreiben Sie den nächsten Abrufzeitpunkt erst nach erfolgreicher Verarbeitung fort. Debuggen Sie Status, Lauf-ID und eine ungefährliche Fehlerbeschreibung – nicht das Access Token oder die vollst ändige Antwort.
msg.statusCode den erwarteten Erfolg anzeigt und msg.payload das fachliche Array der
Beispielantwort enthält. Fehlerstatus müssen in einen kontrollierten Fehlerpfad laufen, statt als leere
Datenlieferung weiterverarbeitet zu werden.
Passende Referenzen
Die Standard-Node mqtt in empfängt neue Nachrichten aus dem bereits geprüften Topic. Bei Bedarf wandelt eine json-Node Zeichenketten oder Buffer in ein Objekt um:
mqtt in (parsed JSON) → Function „Nachricht prüfen“ → nächster Verarbeitungsschritt
1. Broker und Topic konfigurieren
Wählen Sie einen von der Administration für die konkrete Quelle bereitgestellten mqtt-broker-Verbindungsknoten. Host, Port, TLS-Vertrauen und der technische Broker-Zugang müssen dort über den geprüften Konfigurations- und Secret-Weg eingerichtet sein. Tragen Sie produktive Broker-Credentials nicht selbst in den Flow ein.
Die Node mqtt in abonniert für das Beispiel city/traffic/+/counts mit QoS 1. Wählen Sie als Ausgabe a parsed JSON object. Liefert die eingesetzte Konfiguration stattdessen eine Zeichenkette oder einen Buffer, setzen Sie vor die Prüfung eine Standard-Node json.
2. Nachricht technisch prüfen
Die Function Node lehnt fehlende und leere Pflichtwerte ab. Der gültige Zahlenwert 0 bleibt dabei erlaubt:
const row = msg.payload;
if (!row || typeof row !== "object" || Array.isArray(row)) {
throw new Error("MQTT-Payload ist kein JSON-Objekt");
}
const required = ["counting_station_id", "observed_at", "vehicle_count"];
const missing = required.filter((field) =>
row[field] === undefined || row[field] === null || row[field] === ""
);
if (missing.length > 0) {
throw new Error(`MQTT-Nachricht unvollständig: ${missing.join(", ")}`);
}
msg.payload = [row];
msg.source = {
type: "mqtt",
topic: msg.topic,
receivedAt: new Date().toISOString()
};
return msg;
QoS 1 kann eine Nachricht erneut zustellen. Bilden Sie deshalb im folgenden Mapping eine stabile Zielkennung und schreiben Sie idempotent, statt bei jeder Zustellung blind einen neuen Datensatz anzulegen.
eine reale Nachricht aus dem vereinbarten Topic als einzelnes Objekt ankommt und nach der Prüfung als Array
in msg.payload vorliegt. Topic und Empfangszeit bleiben in msg.source erhalten;
ungültige Nachrichten laufen in den Fehlerpfad.
Passende Referenzen
Dieser Weg verwendet die zusätzliche Node amazon s3. Das Beispiel liest verkehrszaehlung-2026-08-19.csv aus dem Bucket verkehr-rohdaten:
Ist amazon s3 nicht in Ihrer Palette vorhanden, lassen Sie die Node durch die Administration
installieren. Melden Sie dafür die betroffene Node-RED-Instanz und den benötigten S3-Zugriff.
Inject → amazon s3 → Function „UTF-8-Text bilden“ → csv → Function „Spalten prüfen“
1. S3-Verbindung konfigurieren
Legen Sie einen aws-config-Verbindungsknoten an. Für den UDSP-Dateispeicher sieht die sachliche Konfiguration so aus:
{
"type": "aws-config",
"name": "UDSP-MinIO",
"endpoint": "https://s3-minio.<MANDANTENDOMAIN>",
"forcepathstyle": true,
"skiptlsverify": false
}
Die Administration stellt die Verbindungskonfiguration und den zulässigen Credential-Weg bereit. Kopieren Sie Access Key und Secret Key nicht in den JSON-Block, eine Function Node oder msg. Für einen eigenen S3-Speicher bleibt der fachliche Ablauf gleich; ändern Sie Endpoint, Bucket, Object Key, Credentials und gegebenenfalls Path-Style.
Die im Beispiel verwendete Erweiterung kann Access Key und Secret Key nur in Node-RED-Credentialfeldern ablegen. Im aktuell geprüften UDSP-Plattformstand ist die Projekt-Credentialverschlüsselung nicht standardmäßig aktiviert. Verwenden Sie deshalb keine produktiven S3-Credentials, bevor die Administration eine verschlüsselte Projektkonfiguration, eine echte Secret-Injektion oder einen dafür geeigneteren Node bereitgestellt und geprüft hat. Ein im Editor maskiertes Feld allein ist kein ausreichender Schutz.
Bei einem benutzerdefinierten S3-Endpoint verwendet node-red-node-aws 0.4.0 effektiv die Standardregion. Schreibt Ihr Anbieter für die Request-Signatur eine andere Region vor, ist dieser Beispiel-Node nicht geeignet; die Administration muss dann einen anderen freigegebenen S3-Node auswählen.
Ein externer S3-Bucket wird von HYPERSENSUS weder angezeigt noch befüllt. Der Betreiber muss die Datei selbst bereitstellen und Node-RED den Netz- und Lesezugriff ermöglichen. Ab dem S3-Leseknoten gilt derselbe Flow wie für den UDSP-Dateispeicher.
2. Objekt lesen und in Text umwandeln
Konfigurieren Sie die Node amazon s3 aus node-red-node-aws als Eingang:
{
"type": "amazon s3",
"name": "Verkehrsdatei lesen",
"aws": "<ID-DER-S3-KONFIGURATION>",
"bucket": "verkehr-rohdaten",
"filename": "verkehrszaehlung-2026-08-19.csv",
"region": "us-east-1"
}
Die Node gibt den Dateiinhalt als Buffer in msg.payload aus. Wandeln Sie ihn in einer Function Node ausdrücklich als UTF-8-Text um:
if (!Buffer.isBuffer(msg.payload)) {
throw new Error("S3-Antwort enthält keinen Datei-Buffer");
}
msg.payload = msg.payload.toString("utf8");
msg.source = {
type: "s3",
bucket: "verkehr-rohdaten",
objectKey: "verkehrszaehlung-2026-08-19.csv"
};
return msg;
3. CSV parsen und erwartete Spalten prüfen
Konfigurieren Sie die Standard-Node csv mit Kopfzeile aus erster Zeile, Trennzeichen ; und Ausgabe als ein Array. Danach enthält msg.payload ein Array von Zeilenobjekten. Prüfen Sie die drei erwarteten Spalten:
const required = ["counting_station_id", "observed_at", "vehicle_count"];
const rows = msg.payload;
if (!Array.isArray(rows) || rows.length === 0) {
throw new Error("Die CSV-Datei enthält keine Datenzeilen");
}
for (const [index, row] of rows.entries()) {
const missing = required.filter((field) =>
row[field] === undefined || row[field] === null || row[field] === ""
);
if (missing.length > 0) {
throw new Error(`CSV-Zeile ${index + 2}: fehlt ${missing.join(", ")}`);
}
}
msg.payload = rows;
return msg;
Das Ergebnis ist noch nicht das Zielmodell, sondern ein kontrollierbarer Rohdatensatz:
[
{
"counting_station_id": "zaehlstelle-017",
"observed_at": "2026-08-19T08:00:00Z",
"vehicle_count": 126
},
{
"counting_station_id": "zaehlstelle-021",
"observed_at": "2026-08-19T08:00:00Z",
"vehicle_count": 84
}
]
Führen Sie „Bucket nicht erlaubt“, „Objekt nicht gefunden“ und „Datei nicht parsebar“ über catch zunächst in einen kontrollierten technischen Fehlerpfad. Unterscheiden Sie dort die Ursache, ohne Access Keys oder vollständige schützenswerte Dateiinhalte im Debug-Fenster auszugeben.
Importierbares S3-Minimalbeispiel anzeigen
Das Beispiel enthält bewusst keine Credentials. Lassen Sie den zulässigen Credential-Weg vor dem Einsatz administrativ bereitstellen und prüfen Sie Endpoint sowie freigegebene Paketversion.
[
{"id":"tab-s3-example","type":"tab","label":"CSV aus S3 laden","disabled":false,"info":""},
{"id":"s3-trigger","type":"inject","z":"tab-s3-example","name":"Datei laden","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"payload":"","payloadType":"date","x":130,"y":100,"wires":[["s3-read"]]},
{"id":"s3-read","type":"amazon s3","z":"tab-s3-example","aws":"s3-config","bucket":"verkehr-rohdaten","filename":"verkehrszaehlung-2026-08-19.csv","region":"us-east-1","name":"Verkehrsdatei lesen","x":340,"y":100,"wires":[["s3-buffer-to-text"]]},
{"id":"s3-buffer-to-text","type":"function","z":"tab-s3-example","name":"UTF-8-Text bilden","func":"if (!Buffer.isBuffer(msg.payload)) {\n throw new Error('S3-Antwort enthält keinen Datei-Buffer');\n}\nmsg.payload = msg.payload.toString('utf8');\nmsg.source = {type: 's3', bucket: 'verkehr-rohdaten', objectKey: 'verkehrszaehlung-2026-08-19.csv'};\nreturn msg;","outputs":1,"x":570,"y":100,"wires":[["s3-csv"]]},
{"id":"s3-csv","type":"csv","z":"tab-s3-example","name":"CSV parsen","spec":"rfc","sep":";","hdrin":true,"hdrout":"none","multi":"mult","ret":"\n","temp":"","skip":"0","strings":true,"include_empty_strings":false,"include_null_values":false,"x":770,"y":100,"wires":[["s3-validate"]]},
{"id":"s3-validate","type":"function","z":"tab-s3-example","name":"Spalten prüfen","func":"const required = ['counting_station_id', 'observed_at', 'vehicle_count'];\nif (!Array.isArray(msg.payload) || msg.payload.length === 0) {\n throw new Error('Die CSV-Datei enthält keine Datenzeilen');\n}\nfor (const [index, row] of msg.payload.entries()) {\n const missing = required.filter((field) => row[field] === undefined || row[field] === null || row[field] === '');\n if (missing.length > 0) {\n throw new Error('CSV-Zeile ' + (index + 2) + ': fehlt ' + missing.join(', '));\n }\n}\nreturn msg;","outputs":1,"x":950,"y":100,"wires":[["s3-result"]]},
{"id":"s3-result","type":"debug","z":"tab-s3-example","name":"Geladene Rohdaten","active":true,"tosidebar":true,"complete":"payload","targetType":"msg","x":1160,"y":100,"wires":[]},
{"id":"s3-catch","type":"catch","z":"tab-s3-example","name":"S3-/CSV-Fehler","scope":["s3-read","s3-buffer-to-text","s3-csv","s3-validate"],"uncaught":false,"x":570,"y":180,"wires":[["s3-error"]]},
{"id":"s3-error","type":"debug","z":"tab-s3-example","name":"Technischer Fehler","active":true,"tosidebar":true,"complete":"error","targetType":"msg","x":800,"y":180,"wires":[]},
{"id":"s3-config","type":"aws-config","name":"S3-Quelle","endpoint":"https://s3-minio.<MANDANTENDOMAIN>","forcepathstyle":true,"skiptlsverify":false}
]
das exakt benannte Objekt als Buffer gelesen, als UTF-8-CSV geparst und als nicht leeres Array mit den drei
erwarteten Spalten in msg.payload abgelegt wurde. Quelle, Bucket und Object Key bleiben in
msg.source nachvollziehbar.
Passende Referenzen
Dieser Weg verwendet die zusätzliche Node postgresql:
Ist postgresql nicht in Ihrer Palette vorhanden, lassen Sie die Node durch die Administration
installieren. Melden Sie dafür die betroffene Node-RED-Instanz und den benötigten Datenbankzugriff.
Inject → Function „Abfrage vorbereiten“ → postgresql → Function „Ergebnis prüfen“
1. Lesende Verbindung verwenden
Verwenden Sie für die Quelle einen ausdrücklich freigegebenen, lesenden Datenbankzugang. Interne Datenbanken von Stellio, FROST oder anderen Plattformkomponenten sind keine allgemeinen Integrationsquellen.
Wählen Sie einen von der Administration für die konkrete Quelle bereitgestellten postgreSQLConfig-Verbindungsknoten. Er enthält Host, Port, Datenbank und TLS-Konfiguration und bezieht den lesenden technischen Zugang über den für die Instanz geprüften Secret-Weg. Tragen Sie keine produktiven Benutzernamen oder Passwörter selbst in den Flow ein, solange dieser Weg nicht abgesichert ist.
2. Begrenzte Abfrage vorbereiten
Die Abfrage bleibt parametriert; Werte werden nicht per Stringverkettung in SQL eingesetzt:
msg.query = `
SELECT
counting_station_id,
observed_at,
vehicle_count
FROM integration.traffic_counts
WHERE observed_at >= $1::timestamptz
ORDER BY observed_at, counting_station_id
LIMIT 1000;
`;
msg.params = ["2026-08-19T00:00:00Z"];
msg.source = {type: "postgresql", relation: "integration.traffic_counts"};
return msg;
Konfigurieren Sie die Node postgresql ohne Ergebnisaufteilung. Sie übernimmt msg.query und msg.params; die Ergebniszeilen stehen danach als Array in msg.payload.
3. Ergebnis prüfen
Die Prüfung akzeptiert 0 als gültigen Zählwert, lehnt aber fehlende, leere oder null gesetzte Pflichtfelder ab:
if (!Array.isArray(msg.payload)) {
throw new Error("Die SQL-Node hat kein Ergebnisarray geliefert");
}
if (msg.payload.length > 1000) {
throw new Error("Die vereinbarte Abfragegrenze wurde überschritten");
}
const required = ["counting_station_id", "observed_at", "vehicle_count"];
for (const row of msg.payload) {
const missing = required.filter((field) =>
row[field] === undefined || row[field] === null || row[field] === ""
);
if (missing.length > 0) {
throw new Error(`Eine Ergebniszeile ist unvollständig: ${missing.join(", ")}`);
}
}
return msg;
Nutzen Sie im wiederholten Betrieb einen gespeicherten, erst nach erfolgreicher Verarbeitung fortgeschriebenen Wasserstand oder Zeitstempel. Das Beispiel verwendet einen festen Zeitpunkt, damit es reproduzierbar getestet werden kann.
die parametrisierte SELECT-Abfrage höchstens die vereinbarte Anzahl vollständiger Zeilen liefert und das
Ergebnis als Array in msg.payload vorliegt. Die verwendete Relation bleibt in
msg.source dokumentiert; Schreibrechte werden für diesen Ladeweg nicht benötigt.
Passende Referenzen
Gemeinsamer Ausgang der vier Beispiele
Nach jedem Beispiel enthält msg.payload ein Array mit demselben Rohdatenvertrag:
[
{
"counting_station_id": "zaehlstelle-017",
"observed_at": "2026-08-19T08:00:00Z",
"vehicle_count": 126
}
]
Die einheitliche Schreibweise hält den Beispielweg über HTTP, MQTT, S3 und PostgreSQL vergleichbar. Liefert eine reale Quelle andere Feldnamen oder eine andere Struktur, wird diese Abweichung im nächsten Schritt ausdrücklich als Mapping behandelt.
Fahren Sie mit Daten verarbeiten fort. Dort werden Pflichtwerte und fachliche Regeln geprüft. Wenn Quell- und Zielstruktur voneinander abweichen, wird das Mapping als Teil dieser Verarbeitung festgelegt und umgesetzt. Der validierte Ausgang wird anschließend unter Daten speichern an das vereinbarte Ziel geschrieben.