mitmario.dev

Warum Express?

Node.js Sandbox 3 Min Lesezeit 3 BeispieleLektion 1 von 7

Am Ende von Lektion 8.7 stand eine Liste: Platzhalter im Pfad, Inhaltstypen für Dateien, und die immer gleichen drei Zeilen in jeder Route. Dieser Abschnitt arbeitet sie ab.

Derselbe Server, zweimal

Zwei Routen von Hand
import { createServer } from "node:http";

const server = createServer((req, res) => {
  const pfad = new URL(req.url, "http://localhost").pathname;

  if (req.method === "GET" && pfad === "/") {
    res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
    res.end("Guten Tag");
    return;
  }

  if (req.method === "GET" && pfad === "/api/zeit") {
    res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
    res.end(JSON.stringify({ stunde: 12 }));
    return;
  }

  res.writeHead(404, { "Content-Type": "text/plain; charset=utf-8" });
  res.end("nicht gefunden");
});

server.listen(3000);

Das kennst du, es ist Abschnitt 8 in klein. Adresse zerlegen, Methode und Pfad vergleichen, Kopfzeilen setzen, antworten, return, und unten der Standardfall.

Dieselben zwei Routen mit Express
import express from "express";

const app = express();

app.get("/", (req, res) => res.send("Guten Tag"));
app.get("/api/zeit", (req, res) => res.json({ stunde: 12 }));

app.listen(3000);

Zwei Zeilen für dasselbe. Und es sind nicht nur weniger Zeichen: Der Content-Type ist gesetzt, ohne dass jemand ihn genannt hat. JSON.stringify steht nirgends. Der Standardfall ist da, obwohl niemand ihn geschrieben hat. Das return kann man nicht vergessen, weil es keins gibt.

Rechts sieht beides gleich aus, und das ist die Aussage: derselbe Server, zweimal. Der Unterschied steckt im Standardfall, also in der Antwort auf einen Pfad, den keine Route bedient. Frag beide Beispiele im Terminal danach, der Befehl steht bei jedem in der Befehlsleiste:

curl -i http://localhost:3000/nix

Der handgeschriebene Server antwortet mit text/plain, weil ich das so hingeschrieben habe. Express antwortet mit text/html, weil sein eingebauter Standardfall eine kleine Fehlerseite schickt. Genau deshalb ersetzt man ihn bei einer Datenschnittstelle meistens, und das ist Lektion 9.2.

Was Express dir abnimmt

  • Routing, inklusive Platzhaltern im Pfad.
  • Den Standardfall, wenn keine Route passt.
  • Inhaltstypen, passend zu dem, was du sendest.
  • JSON senden, in einem Aufruf statt in dreien.
  • Statische Dateien aus einem Ordner, ohne eine Route je Datei.
  • Den Body lesen, also alles, was ein Aufrufer mitschickt. Das ist Abschnitt 10.

Und was es nicht ist

Express ist kein Framework mit Meinung. Es schreibt dir keine Ordnerstruktur vor, bringt keine Datenbankanbindung mit, kein Templating, keine Anmeldung, keine Konfiguration. Es ist eine Bibliothek für den Weg von der Anfrage zur Antwort, und alles andere entscheidest du.

Das ist gleichzeitig seine Stärke und der Grund, warum zwei Express-Projekte selten gleich aussehen. Wer sich einmal in ein fremdes Express-Projekt einarbeitet, muss dessen Struktur lesen, statt sie zu kennen. Abschnitt 18 kommt darauf zurück und schlägt eine vor.

Zur Einordnung: Fastify löst dieselbe Aufgabe mit mehr Augenmerk auf Geschwindigkeit und mitgelieferter Schema-Prüfung. Koa kommt von denselben Leuten wie Express, ist kleiner und baut konsequent auf async. Nest geht in die andere Richtung und bringt genau die Meinung mit, die Express fehlt: Ordnerstruktur, Dekoratoren, Dependency Injection. Wer Express versteht, liest alle drei ohne Anlauf, denn das Grundmuster ist überall dasselbe.

Es ist immer noch node:http

Das ist der Punkt, der dir den ganzen Abschnitt leichter macht.

Darunter liegt weiterhin node:http
import express from "express";
import { Server, IncomingMessage, ServerResponse } from "node:http";

const app = express();

app.get("/", (req, res) => {
  res.type("text/plain").send(
    [
      `req ist eine IncomingMessage:         ${req instanceof IncomingMessage}`,
      `res ist eine ServerResponse:          ${res instanceof ServerResponse}`,
      `req.url gibt es weiterhin:            ${req.url}`,
      `app.listen liefert einen http.Server: ${server instanceof Server}`,
    ].join("\n")
  );
});

const server = app.listen(3000);

Vier Zeilen, viermal true, und rechts kannst du sie nachlesen. app.listen gibt dir denselben http.Server zurück, den createServer dir gegeben hätte. req ist dieselbe IncomingMessage, res dieselbe ServerResponse. Express hat nichts ersetzt, es hat Methoden dazugehängt.

Daraus folgt zweierlei. Erstens: Alles, was du in Abschnitt 8 gelernt hast, gilt weiter. req.headers ist da, res.setHeader ist da, res.end ist da. Zweitens, und das ist im Alltag wichtiger: Wenn etwas nicht funktioniert, kannst du eine Ebene tiefer schauen. Ein Express-Server ist keine Blackbox, sondern ein node:http-Server mit einer Schicht darüber, und du kennst die Schicht darunter.

Genau das ist der Unterschied, den dieser Kurs anders herum aufbaut als die meisten. Wer mit Express anfängt, lernt Methodennamen. Wer mit node:http anfängt, lernt, was die Methodennamen ersetzen.

Zum Mitnehmen

Express nimmt dir den Weg von der Anfrage zur Antwort ab, sonst nichts. Unter der Haube läuft weiterhin node:http, und req und res sind dieselben Objekte wie vorher.

Jetzt du

Basis Konto, kostenlos

Zu dieser Lektion gehört eine Aufgabe. Du schreibst den Code selbst, und nach jedem Lauf sagt dir eine Prüfliste, was schon stimmt.

Dafür brauchst du das Basis Konto. Es kostet nichts, und ein Passwort gibt es auch nicht.

In diesem Kurs läuft dein Code auf einem Server. Dafür hat das Basis Konto 1 Stunde im Monat, mehr Zeit gibt es mit dem Premium Konto.

Was in dieser Lektion steckt

  • Artikel mit 3 Beispielen zum Ausprobieren

    Steht hier, ohne Konto lesbar.

  • Aufgabe, dein Code läuft auf einem Server

    Öffnet sich mit dem Basis Konto.