mitmario.dev

Synthese: die Notizverwaltung

Node.js Sandbox 2 Min Lesezeit 3 BeispieleLektion 6 von 7

Fünf Lektionen, fünf Bausteine: lesen, schreiben, Ordner, JSON, Fehler. Diese Lektion setzt sie zusammen, und zwar zu genau dem Ablauf, den du in kleinen Werkzeugen immer wieder brauchst.

Der Ablauf in vier Schritten

Ordner sicherstellen. mkdir mit recursive: true, und zwar bedingungslos vor dem Schreiben.

mkdir stört sich nicht daran
import { mkdir, readdir } from "node:fs/promises";

await mkdir("daten/2026", { recursive: true });
await mkdir("daten/2026", { recursive: true });

console.log("Zweimal aufgerufen, kein Fehler.");
console.log("Vorhanden:", await readdir("daten"));

Das ist der Grund, warum man diese Option praktisch immer setzt: Der Aufruf beschwert sich nicht, wenn schon alles da ist. Damit brauchst du keine Prüfung davor und keinen try darum.

Laden oder mit einem Standard anfangen. Beim ersten Start gibt es die Datei nicht, und das ist kein Fehler.

Laden oder frisch anfangen
import { readFile } from "node:fs/promises";

async function laden(pfad) {
  try {
    return JSON.parse(await readFile(pfad, "utf8"));
  } catch (fehler) {
    if (fehler.code === "ENOENT") return [];
    throw fehler;
  }
}

const notizen = await laden("daten/notizen.json");

console.log("Geladen:", notizen.length, "Notizen");
console.log("Fängt also frisch an:", notizen.length === 0);

Beachte, was hier passiert und was nicht: ENOENT wird abgefangen und beantwortet, jeder andere Fehler fliegt weiter. Eine kaputte JSON-Datei oder fehlende Rechte sollen dein Programm anhalten, nicht in eine leere Liste verwandeln. Sonst überschreibst du im nächsten Schritt eine Datei voller Daten mit einem leeren Array, und niemand merkt es.

Ändern. Ab hier ist es ganz normales JavaScript: push, find, filter. Die Daten liegen als Objekt im Speicher und wissen nichts mehr von einer Datei.

Ganz zurückschreiben. JSON.stringify und writeFile, in einem Zug.

Warum das Muster funktioniert

Die Stärke dieses Ablaufs ist, dass es zwischen Laden und Schreiben keinen Zwischenzustand gibt. Die Datei ist entweder der alte Stand oder der neue. Du musst dir nie merken, welche Änderungen schon gespeichert sind und welche nicht.

Für ein Kommandozeilenwerkzeug ist das genau richtig. Es läuft, macht eine Sache, schreibt einmal und ist fertig.

Und wo es aufhört

Jetzt der ehrliche Teil.

Wo das Muster aufhört zu tragen
import { mkdir, readFile, writeFile } from "node:fs/promises";

const PFAD = "daten/liste.json";

async function hinzufuegen(text) {
  let liste = [];
  try {
    liste = JSON.parse(await readFile(PFAD, "utf8"));
  } catch {
    liste = [];
  }
  liste.push(text);
  await writeFile(PFAD, JSON.stringify(liste));
}

await mkdir("daten", { recursive: true });

await writeFile(PFAD, "[]");
await hinzufuegen("A");
await hinzufuegen("B");
console.log("Nacheinander gespeichert:", JSON.parse(await readFile(PFAD, "utf8")).length);

await writeFile(PFAD, "[]");
await Promise.all([hinzufuegen("A"), hinzufuegen("B")]);
console.log("Gleichzeitig gespeichert:", JSON.parse(await readFile(PFAD, "utf8")).length);

Zweimal derselbe Vorgang, einmal nacheinander und einmal gleichzeitig. Nacheinander landen beide Einträge in der Datei. Gleichzeitig landet nur einer.

Der Grund ist offensichtlich, sobald man ihn sieht: Beide Abläufe lesen dieselbe leere Liste, beide hängen ihren Eintrag an ihre eigene Kopie an, beide schreiben die ganze Datei. Der zweite Schreiber überschreibt den ersten samt seinem Eintrag. Verloren geht nichts mit einem Knall, sondern still.

Bei einem Kommandozeilenwerkzeug passiert das nie, weil nur ein Ablauf läuft. Bei einem Webserver mit mehreren gleichzeitigen Anfragen passiert es dauernd, und je mehr Betrieb du hast, desto öfter.

Das ist keine Schwäche von JSON und keine von Node, sondern die Eigenschaft von „erst ganz lesen, dann ganz schreiben”. Die Lösung heißt Datenbank: Sie kann einen einzelnen Eintrag ändern, ohne die anderen anzufassen, und sie regelt, wer wann darf. Genau da fängt Abschnitt 12 an, und dann wirst du diesen Wettlauf wiedererkennen.

Bis dahin gilt: Für alles, was du in diesem Kurs baust, reicht die Datei. Aber du weißt jetzt, wo die Grenze liegt, und das ist mehr wert als die Datei selbst.

Zum Mitnehmen

Erst ganz lesen, dann ganz schreiben ist bei kleinen Datenmengen richtig. Ab zwei gleichzeitigen Schreibern ist es kaputt.

Jetzt du

Basis Konto, kostenlos

Zu dieser Lektion gehört eine Aufgabe. Du schreibst den Code selbst, und nach jedem Lauf sagt dir eine Prüfliste, was schon stimmt.

Dafür brauchst du das Basis Konto. Es kostet nichts, und ein Passwort gibt es auch nicht.

In diesem Kurs läuft dein Code auf einem Server. Dafür hat das Basis Konto 1 Stunde im Monat, mehr Zeit gibt es mit dem Premium Konto.

Was in dieser Lektion steckt

  • Artikel mit 3 Beispielen zum Ausprobieren

    Steht hier, ohne Konto lesbar.

  • Aufgabe, dein Code läuft auf einem Server

    Öffnet sich mit dem Basis Konto.