Appearance
Kapitel 12: Fortgeschrittene Praxis
🎯 Lernziele
In diesem Kapitel lernen Sie:
- ✅ TodoList-Backend-API vollständig entwickeln
- ✅ CRUD-Operationen implementieren (Create, Read, Update, Delete)
- ✅ Modularisierung (Code aufteilen)
- ✅ Fehlerbehandlung
- ✅ Node.js mit MySQL-Datenbank verbinden
- ✅
mysql2-Paket verwenden - ✅ TodoList mit MySQL-Datenbank verbinden
Praxis 4: TodoList Backend-API (Vollständige API-Entwicklung)
📝 Anforderungsanalyse
Ziel: Eine vollständige REST-API für eine TodoList-Anwendung erstellen.
Kernfunktionalitäten (CRUD):
- Alle Todos abrufen (GET)
- Einzelnes Todo abrufen (GET)
- Neues Todo erstellen (POST)
- Todo aktualisieren (PUT)
- Todo löschen (DELETE)
🏗️ Projektstruktur
todo-api/
├── server.js # Hauptserver-Datei
├── package.json
├── .env # Umgebungsvariablen
├── data/
│ └── todos.json # "Datenbank" (JSON-Datei)
└── routes/
└── todos.js # (Optional) Routen-Modul📦 package.json erstellen
bash
mkdir todo-api
cd todo-api
npm init -y
npm install express uuidpackage.json:
json
{
"name": "todo-api",
"version": "1.0.0",
"main": "server.js",
"scripts": {
"start": "node server.js",
"dev": "nodemon server.js"
},
"dependencies": {
"express": "^4.18.2",
"uuid": "^9.0.0"
},
"devDependencies": {
"nodemon": "^3.0.2"
}
}💻 Kernimplementierung
server.js:
javascript
const express = require('express');
const fs = require('fs').promises;
const path = require('path');
const { v4: uuidv4 } = require('uuid');
const app = express();
const PORT = 3000;
// Middleware
app.use(express.json());
// "Datenbank"-Pfad
const DB_PFAD = path.join(__dirname, 'data', 'todos.json');
// Hilfsfunktion: Todos aus Datei lesen
async function todosLesen() {
try {
const daten = await fs.readFile(DB_PFAD, 'utf8');
return JSON.parse(daten);
} catch (err) {
// Datei existiert noch nicht → leeres Array zurückgeben
return [];
}
}
// Hilfsfunktion: Todos in Datei schreiben
async function todosSchreiben(todos) {
await fs.writeFile(DB_PFAD, JSON.stringify(todos, null, 2), 'utf8');
}
// === ROUTES ===
// 1. Alle Todos abrufen (GET)
app.get('/api/todos', async (req, res) => {
try {
const todos = await todosLesen();
res.json({ success: true, data: todos });
} catch (err) {
res.status(500).json({ success: false, error: 'Fehler beim Lesen der Todos' });
}
});
// 2. Einzelnes Todo abrufen (GET)
app.get('/api/todos/:id', async (req, res) => {
try {
const todos = await todosLesen();
const todo = todos.find(t => t.id === req.params.id);
if (!todo) {
return res.status(404).json({ success: false, error: 'Todo nicht gefunden' });
}
res.json({ success: true, data: todo });
} catch (err) {
res.status(500).json({ success: false, error: 'Serverfehler' });
}
});
// 3. Neues Todo erstellen (POST)
app.post('/api/todos', async (req, res) => {
try {
const { title, description } = req.body;
// Validierung
if (!title) {
return res.status(400).json({ success: false, error: 'Titel ist erforderlich' });
}
const newTodo = {
id: uuidv4(),
title,
description: description || '',
completed: false,
createdAt: new Date().toISOString()
};
const todos = await todosLesen();
todos.push(newTodo);
await todosSchreiben(todos);
res.status(201).json({ success: true, data: newTodo });
} catch (err) {
res.status(500).json({ success: false, error: 'Fehler beim Erstellen des Todos' });
}
});
// 4. Todo aktualisieren (PUT)
app.put('/api/todos/:id', async (req, res) => {
try {
const { title, description, completed } = req.body;
const todos = await todosLesen();
const todoIndex = todos.findIndex(t => t.id === req.params.id);
if (todoIndex === -1) {
return res.status(404).json({ success: false, error: 'Todo nicht gefunden' });
}
// Todo aktualisieren
if (title !== undefined) todos[todoIndex].title = title;
if (description !== undefined) todos[todoIndex].description = description;
if (completed !== undefined) todos[todoIndex].completed = completed;
await todosSchreiben(todos);
res.json({ success: true, data: todos[todoIndex] });
} catch (err) {
res.status(500).json({ success: false, error: 'Fehler beim Aktualisieren' });
}
});
// 5. Todo löschen (DELETE)
app.delete('/api/todos/:id', async (req, res) => {
try {
const todos = await todosLesen();
const todoIndex = todos.findIndex(t => t.id === req.params.id);
if (todoIndex === -1) {
return res.status(404).json({ success: false, error: 'Todo nicht gefunden' });
}
// Todo löschen
const [gelöschtesTodo] = todos.splice(todoIndex, 1);
await todosSchreiben(todos);
res.json({ success: true, message: 'Todo gelöscht', data: gelöschtesTodo });
} catch (err) {
res.status(500).json({ success: false, error: 'Fehler beim Löschen' });
}
});
// Server starten
app.listen(PORT, () => {
console.log(`🚀 Todo-API läuft auf http://localhost:${PORT}`);
console.log('\nVerfügbare Endpunkte:');
console.log(' GET /api/todos');
console.log(' GET /api/todos/:id');
console.log(' POST /api/todos');
console.log(' PUT /api/todos/:id');
console.log(' DELETE /api/todos/:id');
});🧪 API testen
Mit curl:
bash
# 1. Alle Todos abrufen
curl http://localhost:3000/api/todos
# 2. Neues Todo erstellen
curl -X POST http://localhost:3000/api/todos \
-H "Content-Type: application/json" \
-d '{"title":"Einkaufen","description":"Milch und Brot kaufen"}'
# 3. Todo aktualisieren
curl -X PUT http://localhost:3000/api/todos/<TODO_ID> \
-H "Content-Type: application/json" \
-d '{"completed":true}'
# 4. Todo löschen
curl -X DELETE http://localhost:3000/api/todos/<TODO_ID>Mit Postman:
- Öffnen Sie Postman
- Wählen Sie die HTTP-Methode (GET, POST, PUT, DELETE)
- Geben Sie die URL ein (z.B.
http://localhost:3000/api/todos) - Bei POST/PUT: Body →
raw→JSON→{ "title": "Test" } - Klicken Sie auf Send
Praxis 5: Node.js mit Datenbank verbinden (MySQL)
📝 Anforderungsanalyse
Ziel: TodoList-Daten in einer MySQL-Datenbank speichern (statt JSON-Datei).
🗄️ Datenbank-Grundlagen: MySQL
MySQL installieren
Windows:
- Laden Sie MySQL von mysql.com herunter
- Folgen Sie dem Installations-Assistenten
macOS:
bash
brew install mysql
brew services start mysqlLinux (Ubuntu/Debian):
bash
sudo apt update
sudo apt install mysql-server
sudo systemctl start mysqlDatenbank und Tabelle erstellen
sql
-- Bei MySQL anmelden
mysql -u root -p
-- Datenbank erstellen
CREATE DATABASE todo_app;
USE todo_app;
-- Tabelle erstellen
CREATE TABLE todos (
id VARCHAR(36) PRIMARY KEY,
title VARCHAR(255) NOT NULL,
description TEXT,
completed BOOLEAN DEFAULT FALSE,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
-- Testdaten einfügen
INSERT INTO todos (id, title, description) VALUES
(UUID(), 'Erstes Todo', 'Dies ist ein Test'),
(UUID(), 'Zweites Todo', 'Noch ein Test');
-- Alle Todos anzeigen
SELECT * FROM todos;📦 mysql2-Paket installieren
bash
npm install mysql2💻 Kernimplementierung (mit MySQL)
server-mysql.js:
javascript
const express = require('express');
const mysql = require('mysql2/promise');
const { v4: uuidv4 } = require('uuid');
require('dotenv').config();
const app = express();
const PORT = 3000;
// Middleware
app.use(express.json());
// Datenbank-Verbindungspool erstellen
const pool = mysql.createPool({
host: process.env.DB_HOST || 'localhost',
user: process.env.DB_USER || 'root',
password: process.env.DB_PASSWORD || '',
database: process.env.DB_NAME || 'todo_app',
waitForConnections: true,
connectionLimit: 10,
queueLimit: 0
});
// Hilfsfunktion: Pool verbindung testen
async function dbVerbindungTesten() {
try {
const connection = await pool.getConnection();
console.log('✅ Datenbankverbindung erfolgreich!');
connection.release();
} catch (err) {
console.error('❌ Datenbankverbindung fehlgeschlagen:', err.message);
process.exit(1);
}
}
// === ROUTES ===
// 1. Alle Todos abrufen (GET)
app.get('/api/todos', async (req, res) => {
try {
const [rows] = await pool.execute('SELECT * FROM todos ORDER BY created_at DESC');
res.json({ success: true, data: rows });
} catch (err) {
res.status(500).json({ success: false, error: 'Fehler beim Abrufen der Todos' });
}
});
// 2. Einzelnes Todo abrufen (GET)
app.get('/api/todos/:id', async (req, res) => {
try {
const [rows] = await pool.execute('SELECT * FROM todos WHERE id = ?', [req.params.id]);
if (rows.length === 0) {
return res.status(404).json({ success: false, error: 'Todo nicht gefunden' });
}
res.json({ success: true, data: rows[0] });
} catch (err) {
res.status(500).json({ success: false, error: 'Serverfehler' });
}
});
// 3. Neues Todo erstellen (POST)
app.post('/api/todos', async (req, res) => {
try {
const { title, description } = req.body;
// Validierung
if (!title) {
return res.status(400).json({ success: false, error: 'Titel ist erforderlich' });
}
const id = uuidv4();
await pool.execute(
'INSERT INTO todos (id, title, description) VALUES (?, ?, ?)',
[id, title, description || '']
);
// Erstelltes Todo abrufen
const [rows] = await pool.execute('SELECT * FROM todos WHERE id = ?', [id]);
res.status(201).json({ success: true, data: rows[0] });
} catch (err) {
res.status(500).json({ success: false, error: 'Fehler beim Erstellen des Todos' });
}
});
// 4. Todo aktualisieren (PUT)
app.put('/api/todos/:id', async (req, res) => {
try {
const { title, description, completed } = req.body;
// Prüfen, ob Todo existiert
const [existingRows] = await pool.execute('SELECT * FROM todos WHERE id = ?', [req.params.id]);
if (existingRows.length === 0) {
return res.status(404).json({ success: false, error: 'Todo nicht gefunden' });
}
// Dynamisches Update
const updates = [];
const values = [];
if (title !== undefined) {
updates.push('title = ?');
values.push(title);
}
if (description !== undefined) {
updates.push('description = ?');
values.push(description);
}
if (completed !== undefined) {
updates.push('completed = ?');
values.push(completed);
}
if (updates.length > 0) {
values.push(req.params.id);
await pool.execute(`UPDATE todos SET ${updates.join(', ')} WHERE id = ?`, values);
}
// Aktualisiertes Todo abrufen
const [rows] = await pool.execute('SELECT * FROM todos WHERE id = ?', [req.params.id]);
res.json({ success: true, data: rows[0] });
} catch (err) {
res.status(500).json({ success: false, error: 'Fehler beim Aktualisieren' });
}
});
// 5. Todo löschen (DELETE)
app.delete('/api/todos/:id', async (req, res) => {
try {
// Todo vor dem Löschen abrufen
const [rows] = await pool.execute('SELECT * FROM todos WHERE id = ?', [req.params.id]);
if (rows.length === 0) {
return res.status(404).json({ success: false, error: 'Todo nicht gefunden' });
}
// Todo löschen
await pool.execute('DELETE FROM todos WHERE id = ?', [req.params.id]);
res.json({ success: true, message: 'Todo gelöscht', data: rows[0] });
} catch (err) {
res.status(500).json({ success: false, error: 'Fehler beim Löschen' });
}
});
// Server starten
app.listen(PORT, async () => {
await dbVerbindungTesten();
console.log(`🚀 Todo-API (MySQL) läuft auf http://localhost:${PORT}`);
console.log('\nVerfügbare Endpunkte:');
console.log(' GET /api/todos');
console.log(' GET /api/todos/:id');
console.log(' POST /api/todos');
console.log(' PUT /api/todos/:id');
console.log(' DELETE /api/todos/:id');
});🔐 .env-Datei erstellen
bash
# .env
DB_HOST=localhost
DB_USER=root
DB_PASSWORD=dein_passwort
DB_NAME=todo_appWichtig: Installieren Sie dotenv:
bash
npm install dotenv📝 Zusammenfassung
In diesem Kapitel haben Sie gelernt:
- ✅ Vollständige TodoList-Backend-API mit Express entwickeln
- ✅ CRUD-Operationen (Create, Read, Update, Delete)
- ✅ Fehlerbehandlung implementieren
- ✅ MySQL-Datenbank mit Node.js verbinden
- ✅
mysql2-Paket verwenden - ✅ SQL-Abfragen mit
pool.execute()ausführen
🎯 Nächste Schritte
Im nächsten Kapitel werden wir:
- Häufige Fehler von Node.js-Anfängern analysieren
- Fehlerbehebungs-Techniken lernen
- Debugging-Methoden
📚 Weiterführende Ressourcen
🎉 Kapitel 12 abgeschlossen! Weiter zu Kapitel 13: Häufige Fehler
