Node-RED mit Docker â SQLite Datenbank anbinden und erste Abfragen
Wie du Node-RED in einem Docker-Container startest, habe ich dir bereits im Beitrag âNode-RED mit Docker installieren â Schritt fĂŒr Schrittâ gezeigt.
Nun gehen wir einen Schritt weiter und binden eine Datenbank an, damit wir die im Flow gesammelten Daten dauerhaft speichern können.
https://youtu.be/4X5efvva2I4
Gerade im Zusammenspiel node-red sqlite docker ist SQLite eine ideale Lösung fĂŒr den Einstieg.
Der groĂe Vorteil: SQLite benötigt keinen eigenen Server, sondern speichert alle Daten in einer einzigen Datei. Diese Datei können wir einfach in einem eingebundenen Docker-Volume ablegen und behalten unsere Daten auch nach einem Neustart des Containers.
Damit eignet sich SQLite perfekt fĂŒr:
- Logging von Sensordaten
- Zwischenspeichern von API-Daten
- einfache IoT-Projekte (z. B. mit Shelly oder ESP32)
In diesem Beitrag zeige ich dir Schritt fĂŒr Schritt, wie du eine SQLite-Datenbank in Node-RED einbindest und erste SQL-Abfragen ausfĂŒhrst.
Warum SQLite fĂŒr Node-RED ideal ist
Wenn du mit Node-RED arbeitest, möchtest du Daten hÀufig nicht nur kurzfristig im Flow verarbeiten, sondern auch dauerhaft speichern. Genau hier kommt eine Datenbank ins Spiel.
FĂŒr den Einstieg ist SQLite besonders gut geeignet, da sie im Vergleich zu klassischen Datenbanksystemen wie MySQL oder PostgreSQL deutlich einfacher aufgebaut ist.
Der gröĂte Vorteil: SQLite benötigt keinen eigenen Server.
Alle Daten werden in einer einzigen Datei gespeichert, die direkt im Dateisystem liegt. In unserem Fall bedeutet das, dass wir die Datenbank einfach im Docker-Volume von Node-RED ablegen können â zum Beispiel im Verzeichnis /data.
SQLite Datenbank im Docker Volume abgelegt
Das bringt mehrere Vorteile mit sich:
- Einfache Einrichtung
Keine zusÀtzliche Installation oder Konfiguration eines Datenbankservers notwendig
- Perfekt fĂŒr Docker geeignet
Die Datenbank ist nur eine Datei und kann problemlos ĂŒber ein Volume persistent gespeichert werden
- Ideal fĂŒr kleine bis mittlere Projekte
z. B. Logging von Sensordaten, API-Daten oder einfache Auswertungen
- Geringer Ressourcenverbrauch
SQLite ist sehr leichtgewichtig und lÀuft problemlos auch auf einem Raspberry Pi
Gerade im Zusammenspiel Node-RED SQLite Docker ergibt sich damit eine sehr schlanke und gleichzeitig leistungsfÀhige Lösung, um Daten aus deinen Flows zu speichern und spÀter weiterzuverarbeiten.
FĂŒr gröĂere Projekte mit vielen gleichzeitigen Zugriffen oder komplexen Abfragen kann spĂ€ter immer noch auf Systeme wie MySQL oder PostgreSQL gewechselt werden. FĂŒr den Einstieg und viele IoT-Anwendungen ist SQLite jedoch mehr als ausreichend.
SQLite Nodes in Node-RED installieren
Damit wir eine SQLite-Datenbank in Node-RED verwenden können, benötigen wir zunĂ€chst die passenden Nodes. Diese lassen sich ganz einfach ĂŒber die integrierte Paketverwaltung nachinstallieren.
Ăffne dazu im Node-RED Editor das MenĂŒ oben rechts und gehe auf:
đ MenĂŒ â Palette verwalten â Reiter âInstallierenâ
Im Suchfeld gibst du anschlieĂend sqlite ein. In der Ergebnisliste erscheint das Paket:
đ node-red-node-sqlite
Dieses Paket stellt dir die notwendigen Nodes zur VerfĂŒgung, um SQL-Abfragen direkt innerhalb deiner Flows auszufĂŒhren.
Klicke auf Installieren und warte einen Moment, bis die Installation abgeschlossen ist. In der Regel ist kein Neustart erforderlich â die neuen Nodes stehen sofort im linken Bereich (Palette) zur VerfĂŒgung.
Nach der erfolgreichen Installation findest du die SQLite Node unter den Storage-Nodes und kannst sie per Drag & Drop in deinen Flow ziehen.
Datenbank im Container anlegen und einbinden
FĂŒr dieses Beispiel gehen wir bewusst einen einfachen und direkten Weg, um schnell eine funktionierende SQLite-Datenbank zu erhalten.
Da unser Node-RED Container ein Docker-Volume verwendet, liegt dieses auf dem Host-System im Verzeichnis:
/var/lib/docker/volumes//_data/
In meinem Fall lautet der Pfad:
sudo -i
cd /var/lib/docker/volumes/stefan_node_red_data/_data/
Hier können wir direkt unsere Datenbankdatei anlegen:
AnschlieĂend erstellen wir direkt eine erste Tabelle:
CREATE TABLE IF NOT EXISTS personen (
id INTEGER PRIMARY KEY AUTOINCREMENT,
vorname TEXT NOT NULL,
nachname TEXT NOT NULL,
strasse TEXT,
plz TEXT,
ort TEXT
);
Danach verlassen wir die SQLite-Konsole mit:
Hinweis: Dieser Ansatz ist bewusst einfach gehalten und eignet sich besonders gut fĂŒr Tests und kleinere Projekte. Alternativ könnte man auch direkt im Container arbeiten oder ein eigenes Docker-Image erstellen â fĂŒr unseren Einstieg ist das jedoch nicht zwingend notwendig.
Setzen der Rechte fĂŒr die Datenbank
Wenn du die SQLite-Datenbank manuell im Docker-Volume angelegt hast, kann es passieren, dass Node-RED spÀter nicht in die Datenbank schreiben kann.
SQLITE_READONLY: attempt to write a readonly database
Die Ursache liegt in den Dateirechten:
Die Datenbankdatei wird hÀufig als root erstellt, wÀhrend Node-RED im Container mit einem anderen Benutzer lÀuft.
Rechte im Container anpassen
Um das Problem zu beheben, wechseln wir zunÀchst als root in den Container:
docker exec -it --user root nodered bash
Zugriff auf den Docker Container via bash mit root Rechte
Dann setzen wir die richtigen Rechte fĂŒr die Datenbankdatei und das Verzeichnis:
cd /data
chown 1000:1000 datenbank.db
chown 1000:1000 .
chmod 664 datenbank.db
chmod 755 .
exit
In meinem Fall lĂ€uft der Node-RED Docker-Container mit dem ânormalenâ Benutzer, der beim Einrichten des Linux-Systems erstellt wurde. Dieser Benutzer hat in der Regel die UID 1000.
đ Daher setzen wir den Besitzer der Datei und des Verzeichnisses ebenfalls auf diese UID, sodass Node-RED Schreibzugriff auf die SQLite-Datenbank erhĂ€lt.
SQLite benötigt Schreibrechte auf:
- die Datenbankdatei selbst
- das Verzeichnis, in dem die Datei liegt
Nur wenn beide korrekt gesetzt sind, kann Node-RED Daten speichern.
Datum und Uhrzeit per SQL abfragen
Nachdem wir nun die SQLite Node installiert und unsere Datenbank angelegt haben, erstellen wir im nÀchsten Schritt unseren ersten einfachen Flow in Node-RED.
Ziel ist es, eine erste SQL-Abfrage auszufĂŒhren und das Ergebnis im Debug-Tab anzuzeigen.
FĂŒr diesen ersten Test benötigen wir lediglich drei Nodes:
- Inject Node â startet den Flow und enthĂ€lt das SQL-Statement
- SQLite Node â fĂŒhrt die Abfrage aus
- Debug Node â zeigt das Ergebnis an
Node-RED Flow zum lesen eines Zeitstempels aus einer SQLite Datenbank
Inject Node konfigurieren
In der Inject Node hinterlegen wir direkt unser SQL-Statement im Feld msg.topic:
đ Wichtig: Die SQLite Node erwartet das SQL-Statement im Feld msg.topic.
SQLite Node konfigurieren
Damit die SQLite Node auf unsere Datenbank zugreifen kann, muss diese einmalig konfiguriert werden.
- SQLite Node öffnen
- Auf das Plus-Symbol (+) neben âDatenbankâ klicken
- Pfad zur Datenbank eintragen: /data/datenbank.db
- Mit âHinzufĂŒgenâ und anschlieĂend âFertigâ bestĂ€tigen
đ Die SQLite Node ist nun einsatzbereit und verarbeitet SQL-Statements aus msg.topic.
Die Debug Node muss nicht weiter konfiguriert werden.
StandardmĂ€Ăig lauscht sie bereits auf das Feld:
đ Genau dort liefert die SQLite Node auch das Ergebnis der SQL-Abfrage.
Daten in SQLite speichern
Nachdem wir erfolgreich eine erste SQL-Abfrage ausgefĂŒhrt haben, wollen wir nun Daten in unsere Tabelle personen schreiben.
Dazu erstellen wir einen einfachen Flow mit einer zusÀtzlichen Function Node welche uns zufallsdaten erzeugt.
Node-RED Flow zum speichern von Daten in der SQLite Datenbank
Function Node zum Erzeugen von Zufallsdaten
Die Daten fĂŒr die Tabelle personen könnten natĂŒrlich auch aus einem Formular im Dashboard stammen.
Der Einfachheit halber verwenden wir hier jedoch eine Function Node, die aus einer vordefinierten Liste zufÀllige Werte auswÀhlt und daraus einen Datensatz erzeugt.
const vornamen = ;
const nachnamen = ;
const strassen = ;
const plzOrte = ;
function zufallEintrag(arr) {
return arr;
}
const vorname = zufallEintrag(vornamen);
const nachname = zufallEintrag(nachnamen);
const strasse = zufallEintrag(strassen);
const plzOrt = zufallEintrag(plzOrte);
msg.topic = `
INSERT INTO personen (vorname, nachname, strasse, plz, ort)
VALUES ('${vorname}', '${nachname}', '${strasse}', '${plzOrt.plz}', '${plzOrt.ort}');
`;
return msg;
Dieser Ansatz ist bewusst simpel gehalten und eignet sich hervorragend fĂŒr erste Tests.
FĂŒr produktive Anwendungen â etwa mit Benutzereingaben oder externen Datenquellen â stöĂt diese Methode jedoch schnell an ihre Grenzen.
zufÀllige Daten erzeugt mit der Function Node
đ FĂŒr unseren Einstieg ist sie jedoch vollkommen ausreichend, um die FunktionalitĂ€t der Datenbank zu testen und erste DatensĂ€tze zu erzeugen.
selektieren von DatensĂ€tze ĂŒber SQLite3
Einfaches Dashboard fĂŒr die Tabelle personen
Nachdem wir nun die Tabelle personen in der Datenbank angelegt haben, erstellen wir als NĂ€chstes ein einfaches Dashboard, ĂŒber das neue DatensĂ€tze bequem erfasst werden können.
Node-RED - einfaches Dashboard fĂŒr die Tabelle personen
Das Ziel ist dabei bewusst einfach gehalten:
Wir möchten neue Personen ĂŒber eine kleine Eingabemaske anlegen und die Daten direkt in unserer SQLite-Datenbank speichern.
FĂŒr dieses erste Beispiel konzentrieren wir uns ausschlieĂlich auf das HinzufĂŒgen neuer EintrĂ€ge.
Das Bearbeiten oder Löschen vorhandener DatensĂ€tze wĂŒrde den Flow deutlich komplexer machen und wird deshalb in einem separaten Beitrag behandelt.
Node-RED Flows fĂŒr das Dashboard der Tabelle personen
Node-RED Flows fĂŒr das Dashboard der Tabelle personenHerunterladen
Flow zum speichern von Daten in der Datenbank
Der Flow zum Speichern der Daten in der Datenbank wird einmalig gestartet und stellt ĂŒber das Dashboard ein Eingabeformular bereit. Die eingegebenen Daten werden anschlieĂend in einer Function-Node zu einem INSERT INTO-Statement verarbeitet und danach in die Datenbank geschrieben.
Flow zum speichern von Daten in der Datenbank
Das Eingabeformular lĂ€sst sich ĂŒber die Form-Node recht einfach konfigurieren. Hier definieren wir die einzelnen Felder mit Namen, die beim Absenden an die Function-Node ĂŒbergeben und dort weiterverarbeitet werden.
konfiguration des Fomulars
let vorname = msg.payload.vorname;
let nachname = msg.payload.nachname;
let strasse = msg.payload.strasse;
let postleitzahl = msg.payload.plz;
let ort = msg.payload.ort;
msg.topic = `
INSERT INTO personen (vorname, nachname, strasse, plz, ort)
VALUES ('${vorname}', '${nachname}', '${strasse}', '${postleitzahl}', '${ort}');
`;
return msg;
Die Formulardaten befinden sich im Feld msg.payload und können dort direkt ausgelesen werden.
In diesem einfachen Beispiel verzichten wir bewusst auf eine Validierung oder PrĂŒfung auf doppelte EintrĂ€ge. Die Werte werden lediglich entnommen und im INSERT INTO-Statement an die entsprechenden Stellen eingesetzt.
Am Ende des Flows wird der Prozess zum Aktualisieren der Daten angestoĂen, sodass die Tabelle den neu hinzugefĂŒgten Datensatz direkt anzeigt.
Aufrufen des Flows zum aktualisieren der Tabelle
Flow zum lesen der Daten aus der Datenbank und anzeigen in einer Tabelle
Der Flow zum Aktualisieren der Tabelle mit den Daten aus der Tabelle personen verfĂŒgt ĂŒber zwei EingĂ€nge: Zum einen wird er automatisch gestartet, zum anderen kann er von anderen Flows aus angestoĂen werden.
Im Anschluss folgt eine Function-Node, die das SELECT-Statement erstellt und ĂŒber msg.topic an die SQLite-Node ĂŒbergibt.
Die Function-Node schreibt lediglich das SELECT-Statement in das Feld msg.topic.
Alternativ könnte man dieses auch ĂŒber eine Inject-Node realisieren. Allerdings wĂŒrde das Statement dann fehlen, wenn der Flow von einem anderen Flow aus gestartet wird.
msg.topic = "SELECT * FROM personen;";
return msg;
Konfiguration der Table Node
In der Konfiguration der Table Node legen wir zunÀchst fest das neue Wert die vorhandenen ersetzen, sowie die Spalten NICHT automatisch erkannt werden sollen.
Daraus folgt jedoch das wir im unteren Bereich dann die Spalten manuell anlegen mĂŒssen. Hier können wir uns aus einer zuvor angelegten Debug Node der Felder bedienen welche wir anlegen mĂŒssen.
Flow zum löschen aller DatensÀtze
Da wir derzeit lediglich nur mit Testdaten arbeiten reicht ein knopf zum löschen aller Daten. Dabei ist zu beachten das der Index der Tabelle nicht zurĂŒck gesetzt wird d.h.