Express-Server & REST-API
Einen produktiven Express-Server bauen: Routing, Middleware, JSON, Error-Handling und eine erste REST-API fuer eine Todo-App.
Inhaltsverzeichnis
Express-Server & REST-API
Express ist das meist-genutzte Web-Framework fuer Node.js. Minimalistisch, flexibel, riesige Community. Wir bauen damit eine erste REST-API.
Alternative Frameworks (Fastify, Hono) erwaehnen wir kurz, aber Express ist der beste Startpunkt.
Projekt aufsetzen
mkdir meine-api
cd meine-api
npm init -y
npm install express
npm install -D nodemon
package.json anpassen:
{
"type": "module",
"scripts": {
"dev": "node --watch src/server.js",
"start": "node src/server.js"
}
}
Hello Server
src/server.js:
import express from "express";
const app = express();
const PORT = process.env.PORT ?? 3000;
app.get("/", (req, res) => {
res.send("Hallo, API!");
});
app.listen(PORT, () => {
console.log(`Server laeuft auf http://localhost:${PORT}`);
});
Starten:
npm run dev
Im Browser http://localhost:3000 - โHallo, API!โ.
HTTP-Methoden
app.get("/pfad", handler); // Lesen
app.post("/pfad", handler); // Erstellen
app.put("/pfad", handler); // Vollstaendig aktualisieren
app.patch("/pfad", handler); // Teilweise aktualisieren
app.delete("/pfad", handler); // Loeschen
JSON akzeptieren und liefern
app.use(express.json()); // parst JSON-Bodies
app.post("/echo", (req, res) => {
res.json({ empfangen: req.body, zeit: new Date().toISOString() });
});
Request mit curl:
curl -X POST http://localhost:3000/echo \
-H "Content-Type: application/json" \
-d '{"name": "Anna"}'
Antwort:
{"empfangen":{"name":"Anna"},"zeit":"2026-04-29T10:00:00.000Z"}
Route-Parameter
app.get("/users/:id", (req, res) => {
const id = req.params.id;
res.json({ id, name: "Anna" });
});
GET /users/42 liefert {"id":"42","name":"Anna"}.
Query-Parameter
app.get("/search", (req, res) => {
const q = req.query.q ?? "";
res.json({ query: q });
});
GET /search?q=nodejs โ {"query":"nodejs"}.
Eine Todo-API
Minimalistische REST-API fuer Todos (in-memory):
import express from "express";
const app = express();
app.use(express.json());
let todos = [
{ id: 1, text: "Node lernen", erledigt: false },
{ id: 2, text: "Express ausprobieren", erledigt: false },
];
// Alle
app.get("/todos", (req, res) => {
res.json(todos);
});
// Einzelnes
app.get("/todos/:id", (req, res) => {
const todo = todos.find((t) => t.id === Number(req.params.id));
if (!todo) return res.status(404).json({ error: "Nicht gefunden" });
res.json(todo);
});
// Erstellen
app.post("/todos", (req, res) => {
const { text } = req.body;
if (!text) return res.status(400).json({ error: "text fehlt" });
const todo = {
id: Math.max(0, ...todos.map((t) => t.id)) + 1,
text,
erledigt: false,
};
todos.push(todo);
res.status(201).json(todo);
});
// Aktualisieren
app.patch("/todos/:id", (req, res) => {
const todo = todos.find((t) => t.id === Number(req.params.id));
if (!todo) return res.status(404).json({ error: "Nicht gefunden" });
Object.assign(todo, req.body);
res.json(todo);
});
// Loeschen
app.delete("/todos/:id", (req, res) => {
const i = todos.findIndex((t) => t.id === Number(req.params.id));
if (i === -1) return res.status(404).json({ error: "Nicht gefunden" });
todos.splice(i, 1);
res.status(204).send();
});
app.listen(3000, () => console.log("Server auf :3000"));
Testen:
# Auflisten
curl http://localhost:3000/todos
# Hinzufuegen
curl -X POST http://localhost:3000/todos \
-H "Content-Type: application/json" \
-d '{"text": "Express lernen"}'
# Als erledigt markieren
curl -X PATCH http://localhost:3000/todos/1 \
-H "Content-Type: application/json" \
-d '{"erledigt": true}'
# Loeschen
curl -X DELETE http://localhost:3000/todos/1
Middleware - der Express-Kern
Middleware sind Funktionen, die bei jedem Request durchlaufen werden - Authentifizierung, Logging, CORS etc.
Eigene Middleware
app.use((req, res, next) => {
console.log(`${req.method} ${req.path}`);
next();
});
(req, res, next)- Dreiersatznext()am Ende - wichtig, sonst bleibt der Request haengen
Built-in Middleware
app.use(express.json()); // JSON-Bodies parsen
app.use(express.urlencoded({ extended: true })); // Formulardaten
app.use(express.static("public")); // statische Dateien
Beliebte Middleware-Pakete
npm install cors morgan helmet
import cors from "cors";
import morgan from "morgan";
import helmet from "helmet";
app.use(helmet()); // Security-Header
app.use(cors()); // CORS
app.use(morgan("dev")); // Request-Logging
Async Handler
Vorsicht - Express 4 verschluckt async-Fehler stumm:
app.get("/bad", async (req, res) => {
throw new Error("Oops"); // Express weiss nicht, dass das Promise rejected
});
Express 5 (2024+) fixt das nativ. Fuer Express 4 gibt es Wrapper:
const asyncHandler = (fn) => (req, res, next) =>
Promise.resolve(fn(req, res, next)).catch(next);
app.get("/good", asyncHandler(async (req, res) => {
const data = await fetch("https://api.example.com");
res.json(data);
}));
Error-Handling
Eine spezielle Middleware mit 4 Parametern ist Expressโ Fehler-Handler:
// Ganz am Ende deines Codes:
app.use((err, req, res, next) => {
console.error(err);
res.status(500).json({ error: err.message });
});
Jetzt fangen deine Routes (mit next(err) oder Express-5-Auto-Catch) Fehler zentral ab.
Router - Code organisieren
Bei mehreren Ressourcen wird server.js schnell riesig. Router trennen:
src/routes/todos.js:
import { Router } from "express";
const router = Router();
router.get("/", (req, res) => { /* ... */ });
router.post("/", (req, res) => { /* ... */ });
// ...
export default router;
src/server.js:
import express from "express";
import todosRouter from "./routes/todos.js";
const app = express();
app.use(express.json());
app.use("/todos", todosRouter); // alle Routes unter /todos
app.listen(3000);
Datenbank-Anbindung (kurzer Vorblick)
Mit Prisma oder Drizzle ORM plus Postgres. Beispiel mit einer SQLite-Datenbank via better-sqlite3:
npm install better-sqlite3
import Database from "better-sqlite3";
const db = new Database("todos.sqlite");
db.exec(`
CREATE TABLE IF NOT EXISTS todos (
id INTEGER PRIMARY KEY,
text TEXT NOT NULL,
erledigt INTEGER DEFAULT 0
)
`);
const stmt = db.prepare("INSERT INTO todos (text) VALUES (?)");
stmt.run("Neues Todo");
const alle = db.prepare("SELECT * FROM todos").all();
Im echten Projekt: Prisma oder Drizzle nehmen - typsicher, mit Migrationen.
Alternativen zu Express
- Fastify - schneller, moderner, aehnliche API
- Hono - ultra-schlank, Edge-tauglich, TypeScript-first
- Koa - vom gleichen Autor, moderne async-basiert
Express ist der Standard. Fuer neue Projekte ist Fastify oder Hono oft die schnellere Wahl.
Deployment
Express-Apps deployst du zu:
- Render / Railway / Fly.io - sehr einfach, PaaS
- Vercel (mit Serverless-Wrapper) - automatisch, perfekt mit Next.js
- VPS + Docker - volle Kontrolle
- Heroku (etwas teurer heute)
package.json sollte haben:
{
"scripts": {
"start": "node src/server.js"
}
}
Die Plattform startet via npm start.
Zusammenfassung
- Express mit
app.get/post/put/patch/deleteund Route-Parametern express.json()als Middleware fuer JSON-Bodiesreq.params/req.query/req.bodyzum Auslesen- Middleware als
(req, res, next) => {}oder Error-Handler(err, req, res, next) => {} - Router teilt Code in Ressourcen auf
- CORS, Helmet, Morgan als Must-Have-Middleware
- Fuer neue Projekte: Fastify oder Hono als schnelle Alternativen
Damit hast du die Node-Grundlagen zusammen. Im weiteren Kurs vertiefen wir Datenbanken, Authentifizierung, Testing und Deployment.