Zum Inhalt springen
Node.js Fortgeschritten 30 min

Express-Server & REST-API

Einen produktiven Express-Server bauen: Routing, Middleware, JSON, Error-Handling und eine erste REST-API fuer eine Todo-App.

Aktualisiert:
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) - Dreiersatz
  • next() 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/delete und Route-Parametern
  • express.json() als Middleware fuer JSON-Bodies
  • req.params / req.query / req.body zum 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.

Zurรผck zum Node.js Kurs