Dein erstes Node-Programm
Die wichtigsten Node-APIs: console, process, fs, Argumente, Environment und das erste vernuenftige Skript.
Inhaltsverzeichnis
Dein erstes Node-Programm
Zeit, Node richtig zu nutzen. Wir lernen die wichtigsten eingebauten Module kennen - File-System, Prozess-Infos und wie du ein echtes kleines CLI-Tool baust.
Hello World
index.js:
console.log("Hallo, Node!");
node index.js
# Hallo, Node!
process - Infos zum Prozess
console.log("Node-Version:", process.version); // v22.x.x
console.log("Platform:", process.platform); // darwin, linux, win32
console.log("Prozess-ID:", process.pid);
console.log("Argumente:", process.argv);
console.log("Arbeitsverzeichnis:", process.cwd());
Kommandozeilen-Argumente
node index.js hallo welt
console.log(process.argv);
// ['/usr/bin/node', '/path/to/index.js', 'hallo', 'welt']
const args = process.argv.slice(2); // nur unsere Args
console.log(args); // ['hallo', 'welt']
Environment-Variablen
export MEIN_KEY=geheim
node index.js
const key = process.env.MEIN_KEY;
console.log(key); // "geheim"
const port = process.env.PORT ?? 3000;
Exit
if (args.length === 0) {
console.error("Bitte ein Argument angeben.");
process.exit(1);
}
Nutzereingabe (readline)
import readline from "node:readline/promises";
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
const name = await rl.question("Wie heisst du? ");
console.log(`Hallo, ${name}!`);
rl.close();
node:-Prefix ist gute Praxis fuer eingebaute Module - klar erkennbar als Standard-Library.
Dateien lesen und schreiben
import { readFile, writeFile } from "node:fs/promises";
// Lesen
const inhalt = await readFile("daten.txt", "utf8");
console.log(inhalt);
// Schreiben
await writeFile("ausgabe.txt", "Hallo, Node!\n");
Die fs/promises-API nutzt Promises und funktioniert nativ mit async/await.
Synchron - wennโs mal sein muss
import { readFileSync } from "node:fs";
const inhalt = readFileSync("daten.txt", "utf8");
Blockiert den Event Loop - nur fuer kurze Skripts nutzen, nie in Webservern.
Dateien auflisten
import { readdir } from "node:fs/promises";
const dateien = await readdir("./");
console.log(dateien);
JSON lesen und schreiben
import { readFile, writeFile } from "node:fs/promises";
const daten = JSON.parse(await readFile("config.json", "utf8"));
console.log(daten);
await writeFile(
"output.json",
JSON.stringify({ name: "Anna", alter: 28 }, null, 2)
);
Pfade sauber handhaben
Niemals Pfade manuell zusammensetzen - dafuer gibt es path:
import path from "node:path";
const pfad = path.join("ordner", "unterordner", "datei.txt");
// "ordner/unterordner/datei.txt" (mit passendem Separator)
const absolut = path.resolve("./daten.txt");
const ext = path.extname("foto.jpg"); // ".jpg"
const basis = path.basename("/tmp/foto.jpg"); // "foto.jpg"
const ordner = path.dirname("/tmp/foto.jpg"); // "/tmp"
Und fuer den aktuellen Datei-Pfad:
import { fileURLToPath } from "node:url";
import path from "node:path";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
console.log(__dirname);
In ESM gibt es __filename / __dirname nicht direkt - oben ist das Ersatz-Pattern.
HTTP-Requests
Seit Node 18 hat Node fetch wie der Browser:
const response = await fetch("https://api.github.com/users/vercel");
const daten = await response.json();
console.log(daten.name); // "Vercel"
console.log(daten.location); // "San Francisco"
Ein kleines CLI-Tool
Ein Tool, das Argumente zaehlt und eine Datei schreibt:
import { writeFile } from "node:fs/promises";
import path from "node:path";
const args = process.argv.slice(2);
if (args.length === 0) {
console.error("Aufruf: node zaehle.js <dateiname>");
process.exit(1);
}
const ausgabe = args[0];
const absPath = path.resolve(ausgabe);
const inhalt = `
Erstellt am: ${new Date().toISOString()}
Von: ${process.env.USER ?? "unbekannt"}
Argumente: ${args.length}
`;
await writeFile(absPath, inhalt.trim());
console.log(`Geschrieben: ${absPath}`);
Ausfuehren:
node zaehle.js report.txt
cat report.txt
Shebang fuer direkt ausfuehrbare Skripts
#!/usr/bin/env node
console.log("Direkt ausfuehrbar!");
chmod +x mein-skript.js
./mein-skript.js
Error Handling
import { readFile } from "node:fs/promises";
try {
const inhalt = await readFile("existiert-nicht.txt", "utf8");
} catch (err) {
if (err.code === "ENOENT") {
console.error("Datei nicht gefunden");
} else {
throw err;
}
}
Node-Errors haben einen code-Property (z.B. ENOENT, EACCES) - damit kannst du gezielt reagieren.
Ein kompletteres Beispiel
Ein CLI, das eine JSON-Datei liest, filtert und eine neue schreibt:
#!/usr/bin/env node
import { readFile, writeFile } from "node:fs/promises";
import path from "node:path";
const [quelle, ziel, ...filterArgs] = process.argv.slice(2);
if (!quelle || !ziel) {
console.error("Aufruf: filter.js <quelle.json> <ziel.json> [--min-alter N]");
process.exit(1);
}
const minAlter = parseInt(
filterArgs[filterArgs.indexOf("--min-alter") + 1] ?? "0",
10
);
const leute = JSON.parse(await readFile(quelle, "utf8"));
const gefiltert = leute.filter((p) => p.alter >= minAlter);
await writeFile(ziel, JSON.stringify(gefiltert, null, 2));
console.log(`${gefiltert.length} von ${leute.length} gespeichert in ${ziel}`);
Mit einer leute.json:
[
{"name": "Anna", "alter": 28},
{"name": "Leo", "alter": 17},
{"name": "Max", "alter": 34}
]
node filter.js leute.json erwachsen.json --min-alter 18
# 2 von 3 gespeichert in erwachsen.json
Zusammenfassung
processfuer Args, Env, Exit, Versionnode:fs/promisesfuer moderne async-File-I/Onode:pathfuer plattform-unabhaengige Pfadefetchseit Node 18 eingebautreadlinefuer Konsolen-Input- Error-Codes (
err.code) fuer gezielte Fehlerbehandlung node:-Prefix fuer Standard-Module
Im naechsten Kapitel: npm, Module und das Package-Oekosystem.