Files
uni/slides/dhbw-2026/03_nodejs_basics.md
T
libretech 351e3dfc23 branch-coexist: 3 neue decks 223015b-2026 / 223015c-2026 / dhbw-2026 daneben main
per user-request einen branch der main-content UND rewrite-content trägt — saubere separierung statt symlink/merge-conflict.

struktur:
- slides/<course>/ → main-content restored, eigene assets (kein new-demo-leak)
- slides/<course>-2026/ → rewrite-content, eigene assets (komplett kopiert + new demos)

Makefile: COURSES += 223015b-2026 223015c-2026 dhbw-2026 mit eigenen NAME/KAPITEL/DEPLOY/KLAUSUR-konfig. Klausur für b+c=1, dhbw=leer. Deploy-paths /hdm/223015b-2026 etc.

make build durchlaufen für alle 6 decks, 0 fehler.

dedup: 223015c+dhbw assets identisch in beiden ordnern (kein neu-demos auf b ergänzt). 223015b 86 → 120 files in -2026 (= +34 neue eroeffner/usb-c-drei-achsen/container-vs-codec/etc).
2026-05-14 21:54:15 +02:00

7.2 KiB
Raw Blame History

marp, theme, paginate, backgroundColor, header, footer
marp theme paginate backgroundColor header footer
true gaia true Web Engineering – DHBW Stuttgart Michael Czechowski – SoSe 2026
<style> :root { --color-foreground: #1a1a2e; --color-highlight: #d63384; --color-dimmed: #4a4a6a; } section.invert { --color-foreground: #fff; } section { font-size: 1.4rem; } h1 { color: #a02060; } section.invert h1 { color: #fff; } h2 { color: #1f2937; } pre { background: #0f0f23; color: #f48fb1; border-radius: 8px; border-left: 3px solid #d63384; } pre code { background: transparent; color: inherit; } code { background: #1a1a2e; color: #f48fb1; padding: 0.15em 0.4em; border-radius: 4px; } a { color: var(--color-highlight); } </style>

Kapitel 3 — Node.js Basics

JavaScript außerhalb des Browsers


Was ist Node.js?

Ryan Dahl, 2009. JavaScript-Runtime auf V8 (Chrome's JS-Engine).

Vorher: JS war Browser-only. Mit Node: JS auch als Server, CLI-Tools, Build-Skripte.

Heute: Node ist Standard-Backend für viele Web-Apps. Netflix, LinkedIn, NASA, Walmart.


Warum Node?

Vorteil Was bedeutet das
JS überall gleiche Sprache Frontend + Backend
NPM größtes Package-Ökosystem (~3 Mio Packages)
Non-blocking I/O viele Verbindungen gleichzeitig
Schnell genug V8 optimiert JS aggressiv
Cross-platform Linux, macOS, Windows

Node Versions-Manager

fnm (oder nvm, volta) — pro Projekt Node-Version.

# Installieren (macOS/Linux)
brew install fnm        # oder curl-script

# Aktuelle LTS
fnm install --lts
fnm use lts-latest      # 24.x

# Pro Projekt
echo "24" > .nvmrc
fnm use                 # liest .nvmrc

Aktuell: Node 24 LTS.


Projekt initialisieren

mkdir mein-projekt && cd mein-projekt
npm init -y             # erzeugt package.json

# Abhängigkeiten installieren
npm install express     # → dependencies
npm install -D vitest   # → devDependencies (nur dev)

# Skripte ausführen
npm run start
npm test

-y = alle Defaults akzeptieren.


package.json

{
  "name": "mein-projekt",
  "version": "0.1.0",
  "type": "module",
  "scripts": {
    "dev": "node --watch src/server.js",
    "start": "node src/server.js",
    "test": "vitest"
  },
  "dependencies": {
    "express": "^5.0.0"
  },
  "devDependencies": {
    "vitest": "^2.0.0"
  }
}

"type": "module" aktiviert ESM.


ESM — modern

// utils.js
export function greet(name) {
  return `Hallo ${name}`;
}

export const VERSION = "1.0";

// main.js
import { greet, VERSION } from './utils.js';
console.log(greet('Ada'), VERSION);

Pflicht: .js-Endung in import-Pfad.


CommonJS — Legacy

// utils.cjs
function greet(name) {
  return `Hallo ${name}`;
}
module.exports = { greet };

// main.cjs
const { greet } = require('./utils.cjs');

Wann CJS? Alte Codebases, npm-Packages, die nicht migriert sind. Default heute: ESM.


Built-in Modules

import { readFile } from 'node:fs/promises';
import { createServer } from 'node:http';
import path from 'node:path';
import { fileURLToPath } from 'node:url';

const __dirname = path.dirname(fileURLToPath(import.meta.url));

node: Prefix macht explizit klar — kein npm-Package.


HTTP-Server


Eingebauter HTTP-Server

import { createServer } from 'node:http';

const server = createServer((req, res) => {
  res.writeHead(200, { 'Content-Type': 'text/plain' });
  res.end('Hallo Welt');
});

server.listen(3000, () => {
  console.log('Server läuft auf :3000');
});

Nackt — ohne Framework. Funktioniert.


Express — der Klassiker

import express from 'express';
const app = express();

app.use(express.json());          // parsed JSON-Body
app.use(express.static('public')); // statische Dateien

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

app.post('/api/echo', (req, res) => {
  res.json({ youSent: req.body });
});

app.listen(3000);

REST mit Express

const users = [{ id: 1, name: 'Ada' }];

app.get('/users', (req, res) => res.json(users));

app.get('/users/:id', (req, res) => {
  const user = users.find(u => u.id == req.params.id);
  user ? res.json(user) : res.sendStatus(404);
});

app.post('/users', (req, res) => {
  const user = { id: users.length + 1, ...req.body };
  users.push(user);
  res.status(201).json(user);
});

Alternative Frameworks

Framework Stärke
Express etabliert, größte Community
Fastify schneller, schemas eingebaut
Hono leichtgewichtig, Edge-deployable
Koa von Express-Autoren, modern

Für DHBW: Express oder Hono.


Routing-Pattern

import { Router } from 'express';
const userRouter = Router();

userRouter.get('/',     (req, res) => { /* alle Users */ });
userRouter.get('/:id',  (req, res) => { /* ein User */ });
userRouter.post('/',    (req, res) => { /* User anlegen */ });
userRouter.put('/:id',  (req, res) => { /* User update */ });
userRouter.delete('/:id', (req, res) => { /* User löschen */ });

app.use('/users', userRouter);  // mountet unter /users/*

Fetch in Node


fetch — global ab Node 18

// GET
const res = await fetch('https://api.github.com/users/torvalds');
const data = await res.json();
console.log(data.public_repos);

// POST
const post = await fetch('https://api.example.com/posts', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ title: 'Hallo' })
});

Genau wie im Browser. Kein extra Package (vorher: node-fetch, axios).


Error-Handling

try {
  const res = await fetch(url);
  if (!res.ok) {
    throw new Error(`HTTP ${res.status}`);
  }
  const data = await res.json();
  return data;
} catch (error) {
  console.error('API call failed:', error.message);
  throw error;  // re-throw für caller
}

res.ok ist true für 2xx-Statuscodes.


Environment Variables

// .env (gitignored!)
DATABASE_URL=postgres://...
API_KEY=sk-...
PORT=3000

// src/config.js
import 'dotenv/config';

export const config = {
  port: process.env.PORT || 3000,
  dbUrl: process.env.DATABASE_URL,
  apiKey: process.env.API_KEY,
};

Niemals API-Keys ins Repo committen.


Selbstlernen — Mini-API

  1. Projekt initialisieren: npm init -y, ESM aktivieren
  2. Express installieren
  3. REST-API für eine Ressource (Books / Movies / TODO)
    • GET /items, GET /items/:id, POST /items, DELETE /items/:id
  4. In-Memory-Store als Array (echte DB kommt Kap 4)
  5. Mit Hoppscotch oder curl testen

Bonus: npm run dev mit --watch-Flag.


Zusammenfassung

Heute gelernt:

  • Node.js als JS-Runtime außerhalb Browser
  • fnm + Node 24 LTS als Versions-Setup
  • npm init, package.json, scripts, dependencies
  • ESM (import/export) als modernes Modul-System
  • Built-in HTTP-Server + Express für REST
  • fetch global ab Node 18
  • .env + dotenv für Secrets

Nächste Stunde: async/await, Filesystem, Datenbank, Auth.