Skip to content

Kapitel 14: Fortgeschrittene Techniken

🎯 Lernziele

In diesem Kapitel lernen Sie:

  • ✅ Umgebungsvariablen verwalten (.env)
  • ✅ Fortgeschrittene Fehlerbehandlung (Global, Asynchron)
  • ✅ Middleware entwickeln (Logging, Authentifizierung)
  • ✅ Performance-Optimierung (Blocking vermeiden, Event Loop)
  • ✅ Andere Frameworks kennenlernen (Koa, Nest.js)

14.1 Umgebungsvariablen

📖 Wozu Umgebungsvariablen?

Problem:

javascript
// ❌ Schlecht: Passwort im Code!
const dbPassword = 'meinPasswort123';  // Gefährlich!

Lösung:

javascript
// ✅ Gut: Passwort aus Umgebungsvariable
const dbPassword = process.env.DB_PASSWORD;  // Sicher!

📦 dotenv installieren

bash
npm install dotenv

📄 .env-Datei erstellen

.env:

env
# Datenbank
DB_HOST=localhost
DB_USER=root
DB_PASSWORD=meinPasswort
DB_NAME=meine_datenbank

# Server
PORT=3000
NODE_ENV=development

# API-Keys
API_KEY=mein-api-key

⚠️ Wichtig:

  • .env NIEMALS in Git einchecken!
  • .gitignore erstellen und .env hinzufügen

📥 dotenv verwenden

server.js:

javascript
require('dotenv').config();  // .env laden
const express = require('express');
const app = express();
const port = process.env.PORT || 3000;  // Aus .env oder Standard 3000

// Datenbank-Verbindung (Passwort aus .env)
const dbPassword = process.env.DB_PASSWORD;

app.get('/', (req, res) => {
  res.send(`Server läuft auf Port ${port}`);
});

app.listen(port, () => {
  console.log(`🚀 Server läuft auf http://localhost:${port}`);
  console.log(`Umgebung: ${process.env.NODE_ENV}`);
});

🔍 process.env untersuchen

javascript
// Alle Umgebungsvariablen anzeigen
console.log(process.env);

// Einzelne Variable abrufen
console.log(process.env.DB_HOST);      // 'localhost'
console.log(process.env.PORT);         // '3000'
console.log(process.env.NICHT_VERHANDEN); // undefined

📂 .env-Dateien für verschiedene Umgebungen

mein-projekt/
├── .env              # Standard (Alle)
├── .env.development  # Nur Entwicklung
├── .env.production   # Nur Produktion
└── .env.test         # Nur Tests

dotenv mit spezifischer Datei laden:

javascript
require('dotenv').config({ path: '.env.production' });

14.2 Fortgeschrittene Fehlerbehandlung

🔥 Globale Fehlerbehandlung

Synchroner Code

javascript
// ❌ Schlecht: Fehler wird nicht behandelt
function teilen(a, b) {
  return a / b;  // Wenn b = 0 → Fehler!
}

// ✅ Gut: Try/Catch verwenden
function teilen(a, b) {
  try {
    if (b === 0) {
      throw new Error('Division durch Null ist nicht erlaubt!');
    }
    return a / b;
  } catch (err) {
    console.error('Fehler:', err.message);
    return null;
  }
}

Asynchroner Code (Promises)

javascript
// ❌ Schlecht: Fehler wird ignoriert
fetchDaten().then(daten => {
  console.log(daten);
});

// ✅ Gut: .catch() hinzufügen
fetchDaten()
  .then(daten => {
    console.log(daten);
  })
  .catch(err => {
    console.error('Fehler beim Abrufen der Daten:', err.message);
  });

Asynchroner Code (async/await)

javascript
// ❌ Schlecht: Fehler wird nicht behandelt
async function datenAbrufen() {
  const daten = await fetchDaten();
  console.log(daten);
}

// ✅ Gut: try/catch verwenden
async function datenAbrufen() {
  try {
    const daten = await fetchDaten();
    console.log(daten);
  } catch (err) {
    console.error('Fehler beim Abrufen der Daten:', err.message);
  }
}

🌐 Globale Fehlerbehandlung (Uncaught Exceptions)

javascript
// Unbehandelte Promise-Ablehnungen abfangen
process.on('unhandledRejection', (reason, promise) => {
  console.error('Unbehandelte Promise-Ablehnung:', reason);
  // Hier: Fehler loggen, Server neu starten, etc.
});

// Unbehandelte Ausnahmen abfangen
process.on('uncaughtException', (err) => {
  console.error('Unbehandelte Ausnahme:', err);
  // Hier: Server ordnungsgemäß herunterfahren
  process.exit(1);  // Server beenden
});

🧩 Fehler in Express-Middleware

javascript
const express = require('express');
const app = express();

// Route mit Fehler
app.get('/api/daten', async (req, res, next) => {
  try {
    const daten = await datenbankAbfrage();
    res.json(daten);
  } catch (err) {
    next(err);  // Fehler an Fehler-Middleware weitergeben
  }
});

// Fehler-Middleware (Muss 4 Parameter haben!)
app.use((err, req, res, next) => {
  console.error('Fehler:', err.stack);
  
  res.status(500).json({
    erfolg: false,
    fehler: err.message
  });
});

app.listen(3000);

14.3 Middle ware entwickeln

📦 Logging-Middleware

javascript
// middleware/logger.js
function loggerMiddleware(req, res, next) {
  const startZeit = Date.now();
  
  // Wenn Antwort gesendet wird
  res.on('finish', () => {
    const dauer = Date.now() - startZeit;
    console.log(`[${new Date().toISOString()}] ${req.method} ${req.url} ${res.statusCode} ${dauer}ms`);
  });
  
  next();  // Zur nächsten Middleware weitergehen
}

module.exports = loggerMiddleware;

Verwendung:

javascript
const express = require('express');
const loggerMiddleware = require('./middleware/logger');

const app = express();

// Middleware global anwenden
app.use(loggerMiddleware);

app.get('/', (req, res) => {
  res.send('Startseite');
});

app.listen(3000);

Ausgabe:

[2024-01-15T10:30:00.000Z] GET / 200 5ms
[2024-01-15T10:30:05.000Z] GET /api/daten 200 120ms

🔐 Authentifizierungs-Middleware

javascript
// middleware/auth.js
function authMiddleware(req, res, next) {
  // Token aus Header abrufen
  const token = req.headers['authorization'];
  
  if (!token) {
    return res.status(401).json({ fehler: 'Kein Token bereitgestellt' });
  }
  
  // Token überprüfen (vereinfacht)
  if (token !== 'mein-geheimer-token') {
    return res.status(403).json({ fehler: 'Ungültiger Token' });
  }
  
  // Benutzerinformationen an Request anhängen
  req.benutzer = { id: 1, name: 'Max' };
  
  next();  // Weiter zur Route
}

module.exports = authMiddleware;

Verwendung:

javascript
const express = require('express');
const authMiddleware = require('./middleware/auth');

const app = express();

// Geschützte Route
app.get('/api/profil', authMiddleware, (req, res) => {
  res.json({
    nachricht: 'Profil-Daten',
    benutzer: req.benutzer
  });
});

app.listen(3000);

Testen:

bash
# Ohne Token → 401 Fehler
curl http://localhost:3000/api/profil

# Mit Token → 200 Erfolg
curl -H "Authorization: mein-geheimer-token" http://localhost:3000/api/profil

14.4 Performance-Optimierung

⚠️ Blocking vermeiden

❌ Schlecht: Synchroner Code blockiert Event Loop

javascript
// Blockiert den Event Loop!
const daten = fs.readFileSync('große-datei.txt');  // ❌
console.log('Dies wird erst ACHHER ausgeführt!');

✅ Gut: Asynchroner Code

javascript
// Blockiert nicht!
fs.readFile('große-datei.txt', 'utf8', (err, daten) => {
  if (err) throw err;
  console.log('Datei gelesen');
});
console.log('Dies wird SOFORT ausgeführt!');  // ✅

🔄 Event Loop verstehen

         ┌───────────────────────────┐
         │         Event Loop         │
         └───────────────────────────┘
              ↓          ↓          ↓
    ┌──────────┐ ┌──────┐ ┌────────┐
    │ Timers   │ │ I/O  │ │ Close  │
    │ (setTimeout)                      │
    └──────────┘ └──────┘ └────────┘

Regel: Lange dauernde Operationen (z.B. große Dateien lesen, komplexe Berechnungen) blockieren den Event Loop!


⚡ Performance-Tipps

TippBeschreibung
Asynchron bleibenreadFileSync vermeiden
Streaming verwendenBei großen Dateien fs.createReadStream()
CachingErgebnisse speichern, um erneute Berechnungen zu vermeiden
Cluster-ModulMehrere CPU-Kerne nutzen
Load BalancingLast verteilen (z.B. mit NGINX)

📊 Beispiel: Streaming für große Dateien

javascript
const fs = require('fs');
const http = require('http');
const path = require('path');

const server = http.createServer((req, res) => {
  if (req.url === '/große-datei') {
    // ❌ Schlecht: Alles auf einmal lesen
    // const daten = fs.readFileSync('große-datei.txt');
    // res.end(daten);
    
    // ✅ Gut: Streaming verwenden
    const readStream = fs.createReadStream('große-datei.txt');
    readStream.pipe(res);  // Direkt an Antwort senden
  }
});

server.listen(3000);

14.5 Andere Frameworks

🌿 Koa.js

Was ist Koa?

  • Von den Machern von Express
  • Verwendet async/await (Keine Callbacks!)
  • Sehr dünn und modern

Installation:

bash
npm install koa

Beispiel:

javascript
const Koa = require('koa');
const app = new Koa();

// Middleware
app.use(async (ctx) => {
  ctx.body = 'Hallo von Koa!';
});

app.listen(3000);

🏗️ Nest.js

Was ist Nest.js?

  • Fortgeschrittenes Framework
  • Verwendet TypeScript
  • Ähnlich wie Angular (für Backend)

Installation:

bash
npm i -g @nestjs/cli
nest new mein-projekt

Beispiel:

typescript
// main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  await app.listen(3000);
}
bootstrap();

📊 Vergleich

FrameworkVorteileNachteile
ExpressEinfach, beliebtVeraltet (Callbacks)
KoaModern, async/awaitKleinere Community
Nest.jsTypeScript, strukturiertKomplex für Anfänger

📝 Zusammenfassung

In diesem Kapitel haben Sie gelernt:

  • ✅ Umgebungsvariablen mit dotenv verwalten
  • ✅ Fortgeschrittene Fehlerbehandlung (Global, Asynchron)
  • ✅ Middleware entwickeln (Logging, Authentifizierung)
  • ✅ Performance-Optimierung (Blocking vermeiden, Streaming)
  • ✅ Andere Frameworks (Koa, Nest.js)

🎯 Nächste Schritte

Im nächsten Kapitel werden wir:

  • Häufige Interviewfragen zu Node.js vorbereiten
  • Grundlegende Konzepte wiederholen
  • Fortgeschrittene Themen üben

📚 Weiterführende Ressourcen


🎉 Kapitel 14 abgeschlossen! Weiter zu Kapitel 15: Interviewfragen

Frei für alle Anfänger