dhbw phase 4: alle 8 kapitel slide-MD komplett (4176 zeilen → ~1100 zeilen, ~210 slides total)
kap1 web_eng (104 → 57 slides): kurs-intro + prüfungsleistung-übersicht, internet-timeline (4 milestones), URL→pixel-7-schritte, HTTP, onboarding (node 24 LTS via fnm, brew/winget paket-manager, VS Code/Chromium/Hoppscotch, git setup mit SSH-key, projekt-skeleton), HTML (anatomie, self-closing, grundgerüst, semantik-tabelle, native elemente details/dialog/input-types), A11y volle tiefe (BFSG+EAA 28.06.2025, WCAG-POUR, Perceivable/Operable/Understandable/Robust einzeln mit code, testen mit lighthouse/axe/manuell), CSS-vorschau (anatomie/selektoren/spezifizität/box-model/farben/einheiten/pseudos), JS-vorschau (ECMAScript-timeline/grundlagen/DOM/fetch), frameworks-übersicht (react/vue/svelte/astro counter-vergleich), build-tools/rendering-modi, selbstlernen erste-eigene-seite kap2 css_extended (13 → 28 slides): box-model + box-sizing global, flexbox basics+achsen+item-properties+typische-patterns, grid basics+areas+auto-layout, flexbox-vs-grid tabelle, mobile-first media queries, viewport-meta, fluid sizing mit clamp(), transitions (was performant), keyframe-animationen, prefers-reduced-motion pflicht, CSS-variablen + dark-mode-pattern, CSS functions (calc/clamp/gradient/oklch/filter), CSS-frameworks tabelle + tailwind pro/contra, selbstlernen flexboxfroggy+cssgridgarden kap3 nodejs_basics (~22 → 20 slides): was-ist-node + warum-node-tabelle, fnm versions-manager + node 24 LTS + .nvmrc, npm init + package.json (mit type:module), ESM modern + CJS legacy, built-in modules (node: prefix), HTTP-server eingebaut + express, REST mit express (GET/POST/PUT/DELETE-pattern), alternative frameworks tabelle (fastify/hono/koa), routing-pattern mit Router, fetch global ab node 18, error-handling res.ok, environment variables mit dotenv + niemals committen, selbstlernen mini-API kap4 nodejs_advanced (~24 → 22 slides): synchron-vs-asynchron, promise then-vs-async/await, sequenziell-vs-parallel + Promise.all, promise-methoden tabelle (all/allSettled/race/any), fs/promises lesen+schreiben, streams pipeline für große dateien, DB-übersicht SQLite/Postgres/MongoDB/Redis, better-sqlite3 prepared statements, ORM-übersicht (drizzle/prisma/knex), CRUD-pattern express+sqlite, auth-strategien tabelle, bcrypt für passwörter, JWT-pattern mit httpOnly+secure cookie, try/catch + error-klassen, globaler express-error-handler, selbstlernen REST+DB+auth kap5 testing (~23 → 23 slides): test-pyramide 70%/20%/10%, vitest setup + describe/it/expect, AAA-pattern, matchers tabelle, TDD red-green-refactor mit konkretem beispiel, supertest für API-integration, test-DB :memory: + lifecycle hooks, mocking mit vi.spyOn (sparsam), playwright E2E mit role-based-locators, E2E-vs-unit tradeoffs, coverage realistisch (80% gut), CI mit github-actions, selbstlernen tests-schreiben kap6 typescript (~30 → 19 slides): was-ist-TS + warum-tabelle, setup mit tsx + tsconfig.json + strict:true, basis-typen + any vermeiden, object-typen + interfaces + type-aliases, function-typen (Promise<T>), union + discriminated union + intersection, generics + constraints, built-in generics (Partial/Omit/Record), typ-inference (nicht über-annotieren), typen für REST-API mit express, zod runtime-validation + z.infer, any-vs-unknown-vs-never, best practices, selbstlernen API-zu-TS-migration kap7 docker (~24 → 24 slides): auf-meinem-rechner-läufts problem, container-vs-VM tabelle, docker basics (image/container/dockerfile/registry), dockerfile hallo-welt, schichten + cache-optimierung, multi-stage build, base-image-größen tabelle (alpine ~180MB vs node ~1.1GB), .dockerignore, compose.yml beispiel, compose-commands, networking (service-namen als hostnamen), volumes (named vs bind), env+secrets, github-actions docker-build, registry-übersicht, health-checks, best-practices, selbstlernen app-dockerizen kap8 best_practices (~30 → 28 slides): sinnvolle commits + conventional commits format, .gitignore template, branches+PRs workflow, README-template mit 6 sektionen, SOLID 5 prinzipien tabelle, S-single-responsibility + D-dependency-inversion mit code, 12-factor wichtigste (codebase/deps/config/processes/disposability), factor 3 config-in-env, factor 6 stateless-processes, SemVer MAJOR.MINOR.PATCH, package.json caret/tilde/exakt, code-reviews geben+nehmen + checkliste, doku-quellen tabelle (MDN/nodejs/npm/tc39/caniuse), stack-overflow gut-nutzen, AI-assistenten pro/contra (verstehen-nicht-kopieren), selbstlernen projekt-hygiene-check build geht durch alle 8 kapitel-html+pdf render-spot-check: cover-slide-dark-mode-invert, git-setup code-syntax-highlight, css-anatomie code-blocks — alle sauber kein deploy, nur build+verify
This commit is contained in:
+278
-194
@@ -8,289 +8,373 @@ 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;
|
||||
}
|
||||
section.invert h2 {
|
||||
color: #f48fb1;
|
||||
}
|
||||
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);
|
||||
}
|
||||
: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: invert -->
|
||||
<!-- _header: '' -->
|
||||
<!-- _backgroundColor: #000 -->
|
||||
<!-- _class: lead -->
|
||||
|
||||
# Web Engineering
|
||||
|
||||
**DHBW Stuttgart** · Informatik / Wirtschaftsinformatik
|
||||
|
||||
**Sommersemester 2026** · Sitzung 5 — Express API, CRUD und Middlewares
|
||||
# Kapitel 4 — Node.js Advanced
|
||||
## async · Files · Database · Auth · Errors
|
||||
|
||||
---
|
||||
|
||||
# Ressourcen zum Selbstlernen
|
||||
<!-- _class: lead -->
|
||||
|
||||
- **CODE CRISPIES** — [codecrispi.es](https://codecrispi.es/)
|
||||
- **Online Code-Editor** — [codepen.io](https://codepen.io/pen/)
|
||||
- **MDN** (Mozilla Developer Network) — [developer.mozilla.org/de/](https://developer.mozilla.org/de/)
|
||||
- **Flexbox-Spiel** — [flexboxfroggy.com](https://flexboxfroggy.com/)
|
||||
- **Grid-Spiel** — [cssgridgarden.com](https://cssgridgarden.com/)
|
||||
# Asynchron + Promises
|
||||
|
||||
---
|
||||
|
||||
# Inhalt
|
||||
# Synchron vs. Asynchron
|
||||
|
||||
1. Node.js — Fun Facts & alternative Runtimes
|
||||
2. `process` — Signal-Handling, Graceful Shutdown
|
||||
3. Express — Setup, Routing
|
||||
4. Middlewares (eigene + built-in)
|
||||
5. CRUD-Endpunkte (Create, Read, Update, Delete)
|
||||
6. Error Handling
|
||||
7. WebSockets
|
||||
**Synchron** (blockierend):
|
||||
```javascript
|
||||
const data = readFileSync('huge.txt'); // wartet
|
||||
console.log('weiter'); // erst danach
|
||||
```
|
||||
|
||||
**Asynchron** (non-blocking):
|
||||
```javascript
|
||||
const data = await readFile('huge.txt'); // wartet ohne blockieren
|
||||
console.log('weiter'); // erst danach
|
||||
```
|
||||
|
||||
Node's Stärke: **non-blocking I/O** für viele parallele Operationen.
|
||||
|
||||
---
|
||||
|
||||
<!-- _class: invert -->
|
||||
<!-- _backgroundColor: #000 -->
|
||||
|
||||
# Node.js – Advanced
|
||||
|
||||
---
|
||||
|
||||
# node.js – Fun Facts
|
||||
|
||||
- Entwickelt von Ryan Dahl in 2009
|
||||
- Erste serverseitige Laufzeitumgebung in JavaScript (C++)
|
||||
- Inspiriert von Nginx (Non-blocking I/O)
|
||||
|
||||
**Alternativen heute:**
|
||||
- **Deno** (2020): Rust, Tokio
|
||||
- **Bun** (2022): Zig
|
||||
|
||||
---
|
||||
|
||||
# process – Prozess-Steuerung
|
||||
# Promise — Anatomie
|
||||
|
||||
```javascript
|
||||
process.on("SIGINT", () => {
|
||||
console.log("Received SIGINT. Closing server...");
|
||||
server.close(() => {
|
||||
console.log("Server closed.");
|
||||
process.exit(0);
|
||||
});
|
||||
});
|
||||
const promise = fetch('https://api.example.com');
|
||||
|
||||
// Signale
|
||||
process.exit(0); // Erfolgreich
|
||||
process.exit(1); // Fehler
|
||||
// then-style (alt)
|
||||
promise
|
||||
.then(res => res.json())
|
||||
.then(data => console.log(data))
|
||||
.catch(err => console.error(err));
|
||||
|
||||
// async/await (modern)
|
||||
try {
|
||||
const res = await fetch('https://api.example.com');
|
||||
const data = await res.json();
|
||||
console.log(data);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# node.js Module System
|
||||
# Sequenziell vs. Parallel
|
||||
|
||||
```javascript
|
||||
// ES Modules (modern, "type": "module")
|
||||
import express from "express";
|
||||
import { port } from "./src/config/constants.js";
|
||||
import { readFileSync } from "node:fs";
|
||||
// SEQUENZIELL — langsam (warten + warten + warten)
|
||||
const a = await fetch('/api/a');
|
||||
const b = await fetch('/api/b');
|
||||
const c = await fetch('/api/c');
|
||||
// Gesamtzeit: a + b + c
|
||||
|
||||
// PARALLEL — schnell (alle gleichzeitig starten)
|
||||
const [a, b, c] = await Promise.all([
|
||||
fetch('/api/a'),
|
||||
fetch('/api/b'),
|
||||
fetch('/api/c'),
|
||||
]);
|
||||
// Gesamtzeit: max(a, b, c)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Promise-Methoden
|
||||
|
||||
| Methode | Was |
|
||||
|---------|-----|
|
||||
| `Promise.all([…])` | alle erfolgreich ODER ein Fehler |
|
||||
| `Promise.allSettled([…])` | alle, Resultat = { status, value/reason } |
|
||||
| `Promise.race([…])` | erstes Resultat gewinnt (auch Fehler) |
|
||||
| `Promise.any([…])` | erstes erfolgreiches, ignore Fehler |
|
||||
|
||||
```javascript
|
||||
// CommonJS (legacy)
|
||||
const express = require("express");
|
||||
const fs = require("fs");
|
||||
const fastest = await Promise.race([
|
||||
fetch('/api'),
|
||||
new Promise((_, rej) => setTimeout(() => rej('timeout'), 3000))
|
||||
]);
|
||||
```
|
||||
|
||||
→ Neue Projekte: ESM. Built-ins immer mit `node:`-Präfix.
|
||||
|
||||
---
|
||||
|
||||
# HTTP Request mit node-fetch
|
||||
<!-- _class: lead -->
|
||||
|
||||
# Filesystem
|
||||
|
||||
---
|
||||
|
||||
# fs/promises — Files lesen + schreiben
|
||||
|
||||
```javascript
|
||||
// Node.js 18+ hat fetch eingebaut
|
||||
const res = await fetch('https://api.example.com/data');
|
||||
const data = await res.json();
|
||||
import { readFile, writeFile, readdir, mkdir } from 'node:fs/promises';
|
||||
|
||||
// Ältere Versionen: node-fetch
|
||||
import fetch from 'node-fetch';
|
||||
// Lesen
|
||||
const text = await readFile('input.txt', 'utf-8');
|
||||
const buf = await readFile('image.png'); // Buffer
|
||||
|
||||
// Schreiben
|
||||
await writeFile('output.txt', 'Hallo Welt');
|
||||
await writeFile('data.json', JSON.stringify({ x: 1 }, null, 2));
|
||||
|
||||
// Verzeichnis lesen
|
||||
const files = await readdir('./uploads');
|
||||
|
||||
// Verzeichnis anlegen
|
||||
await mkdir('./tmp', { recursive: true });
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Web Server, Builder, Bundler
|
||||
|
||||
```bash
|
||||
# Boilerplates erstellen
|
||||
npm create vite@latest my-app -- --template react
|
||||
npm create svelte@latest
|
||||
npm create astro@latest
|
||||
|
||||
# nuxt
|
||||
npm install nuxt
|
||||
npm run dev
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
<!-- _header: '' -->
|
||||
<!-- _footer: '' -->
|
||||
|
||||

|
||||
|
||||
|
||||
---
|
||||
|
||||
# Express – Grundlagen
|
||||
# Streams für große Dateien
|
||||
|
||||
```javascript
|
||||
import express from 'express';
|
||||
const app = express();
|
||||
import { createReadStream, createWriteStream } from 'node:fs';
|
||||
import { pipeline } from 'node:stream/promises';
|
||||
|
||||
app.listen(port, () => {
|
||||
console.log(`Server läuft auf Port ${port}`);
|
||||
});
|
||||
// 10 GB Logfile ohne RAM-Explosion verarbeiten
|
||||
await pipeline(
|
||||
createReadStream('huge.log'),
|
||||
// ... transform-stream ...
|
||||
createWriteStream('output.log')
|
||||
);
|
||||
```
|
||||
|
||||
Streams = Daten **stückweise** verarbeiten, nicht alles in RAM.
|
||||
|
||||
---
|
||||
|
||||
# Express – Middleware
|
||||
<!-- _class: lead -->
|
||||
|
||||
# Datenbank
|
||||
|
||||
---
|
||||
|
||||
# Welche DB für DHBW-Projekte?
|
||||
|
||||
| DB | Wann |
|
||||
|----|------|
|
||||
| **SQLite** | klein, embedded, eine Datei. Lokal/dev/demo. |
|
||||
| **PostgreSQL** | Standard für „echte" Apps. Open Source, mächtig. |
|
||||
| **MongoDB** | NoSQL, dokumentenorientiert. Selten erste Wahl. |
|
||||
| **Redis** | Cache, Queue, schnell. |
|
||||
|
||||
Für Prüfungsleistung: **SQLite** (kein Setup) oder **Postgres** (production-realistisch).
|
||||
|
||||
---
|
||||
|
||||
# SQLite mit `better-sqlite3`
|
||||
|
||||
```javascript
|
||||
app.use(express.json());
|
||||
import Database from 'better-sqlite3';
|
||||
const db = new Database('app.db');
|
||||
|
||||
app.use('/api', (req, res, next) => {
|
||||
console.log('Time:', Date.now());
|
||||
next();
|
||||
});
|
||||
db.prepare(`
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
id INTEGER PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||
)
|
||||
`).run();
|
||||
|
||||
app.use((req, res, next) => {
|
||||
console.log('Middleware 2');
|
||||
next();
|
||||
});
|
||||
// Insert
|
||||
const insert = db.prepare('INSERT INTO users (name) VALUES (?)');
|
||||
insert.run('Ada');
|
||||
|
||||
// Query
|
||||
const users = db.prepare('SELECT * FROM users').all();
|
||||
const ada = db.prepare('SELECT * FROM users WHERE id = ?').get(1);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Express – CRUD Routes
|
||||
# ORM / Query-Builder
|
||||
|
||||
Für komplexere Apps: ORM statt Raw-SQL.
|
||||
|
||||
| Tool | Typ |
|
||||
|------|-----|
|
||||
| **Drizzle** | TypeScript-first, modern, lightweight |
|
||||
| **Prisma** | umfangreich, eigenes Schema |
|
||||
| **Knex** | Query-Builder, kein ORM |
|
||||
| **TypeORM** | klassisches ORM |
|
||||
|
||||
```typescript
|
||||
// Drizzle Beispiel
|
||||
const result = await db.select().from(users).where(eq(users.id, 1));
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# CRUD-Pattern mit Express + SQLite
|
||||
|
||||
```javascript
|
||||
app.get('/api/:key', async (req, res) => {
|
||||
const context = cache.get(req.params.key);
|
||||
res.json({ context });
|
||||
app.get('/users', (req, res) => {
|
||||
const users = db.prepare('SELECT * FROM users').all();
|
||||
res.json(users);
|
||||
});
|
||||
|
||||
app.post('/api/:key', async (req, res) => {
|
||||
const payload = req.body;
|
||||
res.json({ created: cache.set(req.params.key, payload) });
|
||||
app.get('/users/:id', (req, res) => {
|
||||
const user = db.prepare('SELECT * FROM users WHERE id = ?').get(req.params.id);
|
||||
user ? res.json(user) : res.sendStatus(404);
|
||||
});
|
||||
|
||||
app.put('/api/:key', async (req, res) => {
|
||||
const payload = req.body;
|
||||
res.json({ updated: cache.update(req.params.key, payload) });
|
||||
});
|
||||
|
||||
app.delete('/api/:key', async (req, res) => {
|
||||
cache.remove(req.params.key);
|
||||
res.json({ deleted: req.params.key });
|
||||
app.post('/users', (req, res) => {
|
||||
const { name } = req.body;
|
||||
const result = db.prepare('INSERT INTO users (name) VALUES (?)').run(name);
|
||||
res.status(201).json({ id: result.lastInsertRowid, name });
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Express – server.listen
|
||||
<!-- _class: lead -->
|
||||
|
||||
# Authentifizierung
|
||||
|
||||
---
|
||||
|
||||
# Auth-Strategien
|
||||
|
||||
| Strategie | Wann |
|
||||
|-----------|------|
|
||||
| **Session-Cookie** | klassische Web-App, Server-Side Render |
|
||||
| **JWT** (JSON Web Token) | API + SPA, stateless |
|
||||
| **OAuth** | „Mit Google einloggen", Drittpartei-Login |
|
||||
| **Magic Link** | Email statt Passwort, modern |
|
||||
| **Passkeys** | passwordless, FIDO2, Zukunft |
|
||||
|
||||
Für DHBW-Projekt: **Session-Cookie** (express-session) ODER **JWT**.
|
||||
|
||||
---
|
||||
|
||||
# Passwörter — *niemals plaintext*
|
||||
|
||||
```javascript
|
||||
const server = app.listen(port, () => {
|
||||
console.log(`Server läuft auf Port ${port}`);
|
||||
});
|
||||
import bcrypt from 'bcrypt';
|
||||
|
||||
server.on('error', (err) => {
|
||||
console.error('Server error:', err);
|
||||
});
|
||||
// Registrieren
|
||||
const hashed = await bcrypt.hash('mein-passwort', 12);
|
||||
db.prepare('INSERT INTO users (email, password) VALUES (?, ?)').run(email, hashed);
|
||||
|
||||
server.close(() => {
|
||||
console.log('Server geschlossen');
|
||||
});
|
||||
// Login
|
||||
const user = db.prepare('SELECT * FROM users WHERE email = ?').get(email);
|
||||
const valid = await bcrypt.compare(password, user.password);
|
||||
```
|
||||
|
||||
**bcrypt** oder **argon2** für Passwort-Hashing. Niemals MD5/SHA1.
|
||||
|
||||
---
|
||||
|
||||
# WebSockets
|
||||
# JWT — kurzer Überblick
|
||||
|
||||
```javascript
|
||||
import { WebSocketServer } from 'ws';
|
||||
import jwt from 'jsonwebtoken';
|
||||
|
||||
const wss = new WebSocketServer({ port: 8080 });
|
||||
// Signieren (beim Login)
|
||||
const token = jwt.sign(
|
||||
{ userId: user.id, role: user.role },
|
||||
process.env.JWT_SECRET,
|
||||
{ expiresIn: '1d' }
|
||||
);
|
||||
res.cookie('token', token, { httpOnly: true, secure: true });
|
||||
|
||||
wss.on('connection', (ws) => {
|
||||
console.log('Client verbunden');
|
||||
// Verifizieren (Middleware)
|
||||
const token = req.cookies.token;
|
||||
const payload = jwt.verify(token, process.env.JWT_SECRET);
|
||||
req.user = payload;
|
||||
```
|
||||
|
||||
ws.on('message', (data) => {
|
||||
console.log('Empfangen:', data.toString());
|
||||
ws.send('Pong');
|
||||
});
|
||||
});
|
||||
`httpOnly + secure + sameSite` — XSS/CSRF-Schutz.
|
||||
|
||||
---
|
||||
|
||||
<!-- _class: lead -->
|
||||
|
||||
# Error-Handling
|
||||
|
||||
---
|
||||
|
||||
# try/catch + Error-Klassen
|
||||
|
||||
```javascript
|
||||
class NotFoundError extends Error {
|
||||
constructor(message) {
|
||||
super(message);
|
||||
this.name = 'NotFoundError';
|
||||
this.status = 404;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const user = await getUser(id);
|
||||
if (!user) throw new NotFoundError(`User ${id} not found`);
|
||||
res.json(user);
|
||||
} catch (err) {
|
||||
if (err instanceof NotFoundError) return res.status(404).json({ error: err.message });
|
||||
console.error(err);
|
||||
res.sendStatus(500);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
<!-- _header: '' -->
|
||||
<!-- _footer: '' -->
|
||||
# Globaler Express-Error-Handler
|
||||
|
||||

|
||||
```javascript
|
||||
// Am Ende aller Routes!
|
||||
app.use((err, req, res, next) => {
|
||||
console.error(err);
|
||||
|
||||
if (err.status) {
|
||||
return res.status(err.status).json({ error: err.message });
|
||||
}
|
||||
|
||||
res.status(500).json({ error: 'Internal Server Error' });
|
||||
});
|
||||
```
|
||||
|
||||
4 Argumente: `(err, req, res, next)` → Express erkennt Error-Handler.
|
||||
|
||||
---
|
||||
|
||||
<!-- _header: '' -->
|
||||
<!-- _footer: '' -->
|
||||
<!-- _class: aufgabe -->
|
||||
|
||||

|
||||
# Selbstlernen — REST-API mit DB
|
||||
|
||||
Erweitert eure Kap-3-API:
|
||||
|
||||
1. **SQLite-DB** mit `better-sqlite3`
|
||||
2. **CRUD** mit DB statt In-Memory-Array
|
||||
3. **Validierung** (z.B. mit `zod`) für POST-Body
|
||||
4. **Auth:** einfache Login-Route mit bcrypt + JWT
|
||||
5. **Global Error-Handler** für saubere Responses
|
||||
|
||||
**Bonus:** Async-Race-Condition simulieren + Promise.all nutzen.
|
||||
|
||||
---
|
||||
|
||||
<!-- _header: '' -->
|
||||
<!-- _footer: '' -->
|
||||
|
||||

|
||||
# Zusammenfassung
|
||||
|
||||
**Heute gelernt:**
|
||||
- **async/await + Promise** sauber anwenden, sequenziell vs. parallel
|
||||
- **fs/promises + Streams** für Dateien
|
||||
- **SQLite + Prepared Statements** als pragmatische DB
|
||||
- **bcrypt** für Passwörter, **JWT** als Token-Pattern
|
||||
- **Error-Klassen + Globaler Handler** in Express
|
||||
|
||||
**Nächste Stunde:** Testing — Vitest, TDD, Integration, E2E.
|
||||
|
||||
Reference in New Issue
Block a user