Zum Inhalt springen
Node.js Anfรคnger 20 min

Dein erstes Node-Programm

Die wichtigsten Node-APIs: console, process, fs, Argumente, Environment und das erste vernuenftige Skript.

Aktualisiert:
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

  • process fuer Args, Env, Exit, Version
  • node:fs/promises fuer moderne async-File-I/O
  • node:path fuer plattform-unabhaengige Pfade
  • fetch seit Node 18 eingebaut
  • readline fuer Konsolen-Input
  • Error-Codes (err.code) fuer gezielte Fehlerbehandlung
  • node:-Prefix fuer Standard-Module

Im naechsten Kapitel: npm, Module und das Package-Oekosystem.

Zurรผck zum Node.js Kurs