PostgreSQL CSV API
Der Node registriert in Node-RED die Route GET /api/opendata/v1/:model.csv und liefert darüber Datensätze aus PostgreSQL als CSV-Datei aus. Welche Schemata, Tabellen und Spalten veröffentlicht werden, legen Sie in einem Modellregister fest; die Datenbankverbindung liegt in einem eigenen Konfigurationsnode. Der Node ist für Open-Data-Angebote gedacht, bei denen ein API-Gateway wie APISIX den öffentlichen Zugang übernimmt.
- Node-Typ
- postgresql-csv-api (Konfigurationsnodes: postgresql-csv-api-registry, postgresql-csv-api-connection)
- Paket
- @htag/node-red-contrib-hypertegrity-endpoints
- Eingänge / Ausgänge
- 0 / 0
- Ziel
- PostgreSQL (optional mit PostGIS)
Wofür Sie den Node verwenden
Kommunen stellen Messdaten ihrer Datenplattform häufig als Open Data bereit. Der PostgreSQL-CSV-API-Node macht Tabellen der Plattformdatenbank, zum Beispiel die zeitliche Datenhaltung des Context Brokers, als CSV-Download zugänglich, ohne dass Sie eigene SQL-Abfragen oder HTTP-Nodes pflegen müssen.
Typische Einsatzfälle sind:
- Monatliche Messreihen veröffentlichen: Wetter-, Pegel- oder Luftqualitätsdaten eines Monats als CSV für ein Open-Data-Portal.
- Jahresdatensätze bereitstellen: Jährlich aggregierte Tabellen mit einem einzigen Aufruf pro Jahr abrufbar machen.
- Sensible Spalten und Geräte ausnehmen: Einzelne Spalten oder
entity_id-Werte werden vom Export ausgeschlossen, Spaltennamen für die Öffentlichkeit umbenannt.
So funktioniert der Node
Route registrieren
Beim Deploy registriert der Node GET /api/opendata/v1/:model.csv am HTTP-Server von Node-RED und holt sich Modelle und Verbindung aus dem Modellregister.
Anfrage prüfen
Der Modellname aus der URL wird im Register nachgeschlagen; nur aktive Modelle werden bedient. Zeitparameter month oder year werden validiert.
Datenbank abfragen
Eine parametrisierte SELECT-Abfrage liest die Zeilen des gewünschten Zeitraums, schließt Entity-IDs aus und ergänzt bei Bedarf PostGIS-Koordinaten.
CSV ausliefern
Ausgeschlossene Spalten werden entfernt, Header umbenannt und die Datei als Download mit Content-Type text/csv gesendet.
Ergebnis: Eine CSV-Datei pro Modell und Zeitraum, live aus PostgreSQL erzeugt, ohne Zwischenspeicherung.
Der Node ist anfragegetrieben: Er hat keine Ein- oder Ausgänge im Flow und wird ausschließlich durch HTTP-Anfragen aktiv. Pro Node-RED-Instanz kann die Route nur einmal registriert werden; ein zweiter Node desselben Typs meldet einen Fehler. Die Datenbankabfrage findet bei jeder Anfrage statt.
Konfiguration
Die Konfiguration verteilt sich auf drei Nodes: den Konfigurationsnode PostgreSQL CSV API: Verbindung, den Konfigurationsnode PostgreSQL CSV API: Modellregister und den eigentlichen Node PostgreSQL CSV API.
PostgreSQL CSV API: Verbindung (postgresql-csv-api-connection)
| Feld | Typ | Standard | Bedeutung |
|---|---|---|---|
| Name | Text | leer | Anzeigename; sonst <Host>:<Port>/<Datenbank>. |
| Host | Text | ${DB_HOST} | Hostname oder IP des PostgreSQL-Servers. Platzhalter der Form ${VAR} werden aus der Umgebung aufgelöst; leer bedeutet die Umgebungsvariable DB_HOST. |
| Port | Zahl | 5432 | Port des Servers; leer bedeutet DB_PORT, sonst 5432. |
| Datenbank | Text | ${DB_NAME} | Name der Datenbank; leer bedeutet DB_NAME. |
| Benutzer | Text (Credential) | leer | Datenbankbenutzer, verschlüsselt in flows_cred.json; leer bedeutet DB_USER. |
| Passwort | Passwort (Credential) | leer | Passwort, verschlüsselt gespeichert; leer bedeutet DB_PASSWORD. |
Der Verbindungspool (maximal 10 Verbindungen, Verbindungs-Timeout 10 s, application_name = node-red-postgresql-csv-api) wird beim ersten Zugriff angelegt und von allen Nodes geteilt, die diese Verbindung referenzieren. Die Verbindung zur Datenbank erfolgt ohne SSL.
PostgreSQL CSV API: Modellregister (postgresql-csv-api-registry), Registerkarte Datenbank
| Feld | Typ | Standard | Bedeutung |
|---|---|---|---|
| Name | Text | leer | Anzeigename des Registers. |
| PostgreSQL | Konfigurationsnode | keiner | Verbindung für Export und Editor-Abfragen. Ohne Auswahl werden die Umgebungsvariablen DB_HOST, DB_PORT, DB_NAME, DB_USER und DB_PASSWORD verwendet. |
| Verbindung testen | Schaltfläche | – | Prüft die Verbindung über den Admin-Endpunkt /postgresql-csv-api/test-connection und zeigt Datenbankname und Serverversion. Die Verbindung muss dafür bereits deployt sein. |
Modellregister, Registerkarte Modelle
Links listet der Dialog alle Modelle mit Suchfeld und Schaltfläche Modell hinzufügen; rechts bearbeiten Sie das ausgewählte Modell. Die Modelle werden als JSON in der Node-Eigenschaft models gespeichert.
| Feld | Typ | Standard | Bedeutung |
|---|---|---|---|
| Öffentlicher Modellname | Text | leer | Bildet den Pfad des Endpunkts, zum Beispiel weather-observed. Erlaubt sind Kleinbuchstaben, Ziffern und Bindestriche (^[a-z0-9]+(?:-[a-z0-9]+)*$); Namen müssen eindeutig sein. |
| Status: Aktiv | Kontrollkästchen | aktiv | Nur aktive Modelle werden ausgeliefert; inaktive beantwortet der Endpunkt mit 400. |
| Schema | Text | leer | PostgreSQL-Schema der Tabelle. |
| Tabelle | Text | leer | Tabellenname. |
| Zeitspalte | Text | leer | Spalte mit dem Zeitstempel, nach der gefiltert und sortiert wird. |
| Frequenz | Auswahl | Monatlich | month: Export je Kalendermonat, Parameter month. year: Export je Kalenderjahr, Parameter year. |
| Point-Spalte (optional) | Text | leer | PostGIS-Geometriespalte; der Export ergänzt daraus die Spalten longitude und latitude (ST_X/ST_Y), fehlende Geometrien ergeben 0.0. |
| Spalten ausschließen | Unteransicht | leer | Spalten laden liest die Spalten der Tabelle über /postgresql-csv-api/columns; angehakte Spalten fehlen in der CSV. Umbenannte Spalten lassen sich nicht ausschließen. |
| Entitäten ausschließen | Unteransicht | leer | Entity-IDs laden liest über /postgresql-csv-api/entities die eindeutigen, nicht leeren Werte der Spalte entity_id aus dem vorherigen vollständigen Monat beziehungsweise Jahr; angehakte IDs werden nicht exportiert. IDs können nicht manuell eingegeben werden; gespeicherte IDs, die im geladenen Zeitraum fehlen, bleiben sichtbar. |
| Spalten umbenennen | Unteransicht | leer | Zuordnungen Quellspalte → öffentlicher Header. Quellspalten müssen gültige Bezeichner sein, Header dürfen beliebigen Text enthalten, aber nicht leer oder doppelt sein. longitude und latitude sind reserviert. |
Schema, Tabelle, Zeitspalte, Point-Spalte und ausgeschlossene Spalten müssen dem Muster ^[a-z_][a-z0-9_]*$ entsprechen. Der Dialog lehnt das Speichern ungültiger Modelle ab („Bitte eindeutige Modellnamen und gültige PostgreSQL-Bezeichner verwenden“); die Laufzeit prüft dieselben Regeln erneut beim Deploy.
PostgreSQL CSV API (postgresql-csv-api)
| Feld | Typ | Standard | Bedeutung |
|---|---|---|---|
| Name | Text | leer | Anzeigename des Nodes im Flow. |
| Register | Konfigurationsnode | – | Modellregister mit Modellen und Verbindung (Pflichtfeld). |
Der Dialog zeigt zur Information die registrierte Route GET /api/opendata/v1/:model.csv.
Der Datenbankbenutzer benötigt USAGE auf jedem konfigurierten Schema und SELECT auf jeder veröffentlichten Tabelle sowie Lesezugriff auf information_schema.columns. Schreibrechte sind nicht erforderlich. Verwenden Sie einen eigenen, nur lesenden Datenbankbenutzer.
Eingang
Der Node hat keinen Eingang im Flow. Er wird durch HTTP-Anfragen an die registrierte Route ausgelöst:
| Element | Typ | Bedeutung |
|---|---|---|
GET /api/opendata/v1/<Modellname>.csv | Route | <Modellname> ist der öffentliche Modellname eines aktiven Modells im Register. |
?month=YYYY-MM | Query-Parameter | Nur bei Frequenz Monatlich: gewünschter Kalendermonat. Ohne Parameter wird der vorherige vollständige Monat exportiert. |
?year=YYYY | Query-Parameter | Nur bei Frequenz Jährlich: gewünschtes Kalenderjahr. Ohne Parameter das vorherige vollständige Jahr. |
| Authentifizierung | – | Der Node selbst prüft keine Zugangsdaten und keine API-Schlüssel. Die Route liegt auf httpNode und unterliegt damit nur den in settings.js konfigurierten Einstellungen httpNodeAuth beziehungsweise httpNodeMiddleware. Für den öffentlichen Zugang ist ein API-Gateway vorgesehen (siehe Hinweise). |
Zeitraumgrenzen werden in der Zeitzone Europe/Berlin berechnet. Zukünftige Monate oder Jahre werden mit 400 abgelehnt.
Ausgang
Der Node sendet keine Nachricht in den Flow. Die HTTP-Antwort ist eine CSV-Datei mit Content-Type: text/csv; charset=utf-8, Content-Disposition: attachment; filename="<Modellname>-<Zeitraum>.csv" und Cache-Control: no-store.
"entity_id","time_index","temperature","relative_humidity","longitude","latitude"
"urn:ngsi-ld:WeatherObserved:station-01","2026-09-01T00:10:00.000Z","14.2","81","8.353","50"
"urn:ngsi-ld:WeatherObserved:station-01","2026-09-01T00:20:00.000Z","14.1","82","8.353","50"
| Element | Bedeutung |
|---|---|
| Kopfzeile | Alle Spalten der Tabelle (SELECT t.*) in Tabellenreihenfolge ohne ausgeschlossene Spalten, bei Point-Spalte ergänzt um longitude und latitude; Header gemäß Umbenennungen. |
| Zeilen | Aufsteigend nach der Zeitspalte sortiert. Jeder Wert steht in doppelten Anführungszeichen, enthaltene Anführungszeichen werden verdoppelt. NULL wird leer ausgegeben, Datumswerte als ISO 8601, Objekte und Arrays als JSON. Zeilentrenner ist CRLF. |
| Leerer Zeitraum | Liefert eine CSV, die nur die Kopfzeile enthält; die Spalten stammen dann aus information_schema.columns. |
| HTTP 200 | Erfolgreicher Export. |
| HTTP 400 | Unbekanntes oder inaktives Modell (Unsupported OpenData model.), ungültiger Zeitparameter (Invalid month. Use YYYY-MM., Invalid year. Use YYYY.) oder zukünftiger Zeitraum (Future months are not available., Future years are not available.) als JSON {"error": "…"}. |
| HTTP 500 | Datenbank- oder Konfigurationsfehler, zum Beispiel fehlende umbenannte Quellspalte oder doppelte Header. Die Antwort lautet immer The OpenData export is currently unavailable.; Details stehen nur im Node-RED-Protokoll. |
Beispiel-Flow
Das Paket bringt einen Beispiel-Flow mit. Sie importieren ihn in Node-RED über Menü → Importieren → Beispiele → @htag/node-red-contrib-hypertegrity-endpoints → postgresql-csv-api. Alternativ laden Sie die Datei postgresql-csv-api.json herunter und importieren sie über Menü → Importieren. Nach dem Import legen Sie im Bearbeitungsdialog die Verbindung, das Register und mindestens ein Modell an.
Verbindung anlegen
Im Register wählen Sie unter PostgreSQL „Neu hinzufügen“ und tragen Host, Datenbank und Zugangsdaten ein oder nutzen die DB_*-Umgebungsvariablen.
Modell definieren
Auf der Registerkarte Modelle legen Sie Modellname, Schema, Tabelle, Zeitspalte und Frequenz fest und schließen Spalten oder Entitäten aus.
Deploy und Abruf
Nach dem Deploy liefert GET /api/opendata/v1/<Modellname>.csv die Datei, zum Beispiel per Browser oder curl.
Beispiel-Flow als JSON anzeigen
[
{"id": "tab1", "type": "tab", "label": "Endpoints", "disabled": false, "info": ""},
{"id": "api1", "type": "postgresql-csv-api", "z": "tab1", "name": "", "x": 200, "y": 100, "wires": []},
{"id": "c1", "type": "comment", "z": "tab1", "name": "PostgreSQL CSV API: Creates an API Endpoint for PostgreSQL in Nodered", "info": "", "x": 320, "y": 40, "wires": []}
]
Status und Fehler
| Anzeige | Bedeutung |
|---|---|
| Grüner Punkt „GET /api/opendata/v1/:model.csv“ (API-Node) | Route registriert, Verbindungspool bereit. |
| Roter Ring „missing registry or httpNode“ (API-Node) | Kein Register ausgewählt oder Node-RED stellt keinen httpNode-Server bereit. |
| Roter Ring „duplicate route“ (API-Node) | Ein zweiter PostgreSQL-CSV-API-Node ist bereits aktiv. |
| Roter Ring „database unavailable“ (API-Node) | Die ausgewählte Verbindung ist noch nicht deployt (Could not create PostgreSQL pool: …). |
| Grüner Punkt „2 model(s)“ (Register) | Modelle erfolgreich geladen und validiert. |
| Roter Ring „invalid configuration“ (Register) | Ein Modell verletzt die Bezeichnerregeln oder ein Modellname ist doppelt; Details im Protokoll (Invalid schema for …, Duplicate public model name: …). |
| Fehler „PostgreSQL pool error: …“ (Verbindung) | Fehler im Verbindungspool, zum Beispiel Verbindungsabbruch. |
| Fehler im Protokoll bei HTTP 500 | Die Ursache (Datenbankfehler, Configured renamed column does not exist: …, Configured output column names are not unique) wird nur protokolliert, nicht an den Client gesendet. |
Hinweise
- Schutz vor SQL-Injection: Schema, Tabelle, Zeitspalte, Point-Spalte und Spaltennamen werden vor der Verwendung gegen das Muster
^[a-z_][a-z0-9_]*$geprüft (Allowlist) und nur dann in die Abfrage eingesetzt. Der Modellname aus der URL wird nie in SQL verwendet, sondern nur im Register nachgeschlagen. Zeitgrenzen und ausgeschlossene Entity-IDs gehen als Abfrageparameter ($1,$2, …) an PostgreSQL. Freie Filterparameter gibt es nicht. - Zugriffsschutz: Der Node authentifiziert Anfragen nicht. Node-RED darf nicht direkt aus dem öffentlichen Netz erreichbar sein. Vorgesehen ist ein API-Gateway (APISIX), das den öffentlichen Präfix
/opendata/v1/*auf/api/opendata/v1/*umschreibt, einen API-Schlüssel (key-auth, HeaderX-API-Key) prüft und nur diese Route veröffentlicht. Der Node-RED-Editor darf nicht über das Gateway erreichbar sein. - Admin-Endpunkte: Spalten laden, Entity-IDs laden und Verbindung testen laufen über
httpAdminund verlangen die Berechtigungflows.readdes Node-RED-Editors. Fehlermeldungen daraus nennen keine Datenbankdetails. - Voraussetzungen an die Tabelle: Für das Ausschließen von Entitäten und für Entity-IDs laden muss die Tabelle eine Spalte
entity_idbesitzen. Für die Point-Spalte wird die Erweiterung PostGIS benötigt (ST_X,ST_Y). Ausgeschlossene Spalten werden aus der Datenbank gelesen, aber nicht ausgegeben. - Zeiträume: Monats- und Jahresgrenzen werden mit
make_timestamptz(…, 'Europe/Berlin')gebildet; Zeitstempel in der CSV erscheinen in UTC (ISO 8601). - Last: Jede Anfrage löst eine vollständige Abfrage des Zeitraums aus; es gibt keinen Cache (
Cache-Control: no-store). Für große Tabellen empfiehlt sich ein Index auf der Zeitspalte und gegebenenfalls Caching im Gateway. - Datenschutz: Exportierte Tabellen können personenbezogene Daten enthalten, etwa Geräte, die einzelnen Haushalten zugeordnet sind, oder Standorte einzelner Personen. Der Node entfernt nichts automatisch. Prüfen Sie jedes Modell vor der Veröffentlichung, schließen Sie betroffene Spalten und
entity_id-Werte aus und dokumentieren Sie die Freigabe als Open Data. - Einheiten: Werte werden unverändert aus der Datenbank übernommen; benennen Sie Spalten mit Einheiten um, zum Beispiel
Temperatur (°C).