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).
362 lines
7.2 KiB
Markdown
362 lines
7.2 KiB
Markdown
---
|
||
marp: true
|
||
theme: gaia
|
||
paginate: true
|
||
backgroundColor: #fff
|
||
header: "Web Engineering – DHBW Stuttgart"
|
||
footer: "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>
|
||
|
||
<!-- _class: lead -->
|
||
|
||
# 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.
|
||
|
||
```sh
|
||
# 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
|
||
|
||
```sh
|
||
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
|
||
|
||
```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
|
||
|
||
```javascript
|
||
// 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
|
||
|
||
```javascript
|
||
// 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
|
||
|
||
```javascript
|
||
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.
|
||
|
||
---
|
||
|
||
<!-- _class: lead -->
|
||
|
||
# HTTP-Server
|
||
|
||
---
|
||
|
||
# Eingebauter HTTP-Server
|
||
|
||
```javascript
|
||
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
|
||
|
||
```javascript
|
||
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
|
||
|
||
```javascript
|
||
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
|
||
|
||
```javascript
|
||
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/*
|
||
```
|
||
|
||
---
|
||
|
||
<!-- _class: lead -->
|
||
|
||
# Fetch in Node
|
||
|
||
---
|
||
|
||
# fetch — global ab Node 18
|
||
|
||
```javascript
|
||
// 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
|
||
|
||
```javascript
|
||
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
|
||
|
||
```javascript
|
||
// .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.
|
||
|
||
---
|
||
|
||
<!-- _class: aufgabe -->
|
||
|
||
# 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.
|