Abschnitt 11 · Lektion 2
Daten lesen
Zwei Routen gehören von jetzt an zusammen: die Sammlung und das Einzelstück. /buecher gibt alle
zurück, /buecher/2 genau eines. Das klingt nach derselben Aufgabe in zwei Größen, aber die beiden
verhalten sich im Fehlerfall grundverschieden, und genau daran erkennt man eine sorgfältig gebaute
Schnittstelle.
Leer ist nicht dasselbe wie nicht vorhanden
import express from "express";
const app = express();
const buecher = [];
const regale = [{ id: 1, name: "Wohnzimmer" }];
// Die Sammlung gibt es, sie ist nur leer. Das ist ein gueltiges Ergebnis.
app.get("/buecher", (req, res) => {
res.json({ anzahl: buecher.length, daten: buecher });
});
// Das Einzelstueck gibt es nicht. Das ist kein gueltiges Ergebnis.
app.get("/regale/:id", (req, res) => {
const regal = regale.find((eintrag) => eintrag.id === Number(req.params.id));
if (regal === undefined) {
return res.status(404).json({ fehler: "nicht gefunden" });
}
res.json(regal);
});
app.listen(3000); Rechts steht die Liste. Vergleich /regale/1 und /regale/99 in der Adresszeile: einmal ein leeres
Regal, einmal gar kein Regal, und das sind zwei verschiedene Antworten.
Eine leere Sammlung ist 200 mit einer leeren Liste, niemals 404. Die Sammlung gibt es ja. Sie hat im Moment nur nichts drin, und das ist eine völlig normale Auskunft: Du hast keine Bücher erfasst.
Beim Einzelstück ist es umgekehrt. /regale/99 ist kein leeres Regal, sondern gar keines. Hier ist
404 richtig, und zwar auf den Punkt: Unter dieser Adresse liegt nichts.
Der Unterschied wird praktisch, sobald jemand deine Schnittstelle benutzt. Wer eine Liste holt und bei leer einen 404 bekommt, muss diesen Fall wie einen Fehler behandeln, obwohl nichts kaputt ist. Nach ein paar solchen Stellen behandelt er 404 grundsätzlich als „egal, weitermachen”, und dann geht ihm der echte Fehler unter.
Nacktes Array oder ein Objekt drumherum
Jetzt eine Entscheidung, die du einmal triffst und danach nie wieder anfasst.
import express from "express";
const app = express();
const buecher = [
{ id: 1, titel: "Node in der Praxis" },
{ id: 2, titel: "JavaScript von vorn" },
];
// Der eine Weg: die Liste ohne alles drumherum.
app.get("/nackt", (req, res) => res.json(buecher));
// Der andere: ein Objekt, in dem noch Platz ist.
app.get("/umhuellt", (req, res) => {
res.json({ anzahl: buecher.length, daten: buecher });
});
// Ein halbes Jahr spaeter kommt Blaetterung dazu. Nur einer der beiden
// Wege vertraegt das, ohne jeden Aufrufer zu brechen.
app.get("/umhuellt-spaeter", (req, res) => {
res.json({ anzahl: buecher.length, seite: 1, seiten: 1, daten: buecher });
});
app.listen(3000); Die drei Formen liegen unter /nackt, /umhuellt und /umhuellt-spaeter. Ruf sie nacheinander auf,
der Unterschied steht direkt vor dir.
Die erste Route gibt die Liste heraus, wie sie ist. Kurz, direkt, gut zu lesen. Die zweite packt sie in ein Objekt und schreibt die Anzahl daneben.
Die dritte Route zeigt, warum dieser Kurs die zweite Form nimmt. Irgendwann kommt Blätterung dazu,
oder ein Zeitstempel, oder ein Hinweis, dass gefiltert wurde. In das Objekt passt das hinein, und
wer nur daten ausliest, merkt nichts davon. Beim nackten Array gibt es keinen Platz: Du müsstest
die Antwort umbauen, und alle, die sie schon auswerten, bauen mit um.
Beide Formen sind richtig, aber nur eine je Schnittstelle. Eine API, in der manche Routen ein Array liefern und andere ein Objekt, kostet jeden Aufrufer bei jeder Route eine Nachfrage.
Filtern über die Adresse
Query-Parameter kennst du aus Lektion 8.6, dort noch mit URL von Hand zerlegt. Express legt sie
dir als req.query hin.
import express from "express";
const app = express();
const buecher = [
{ id: 1, titel: "Node in der Praxis", jahr: 2024 },
{ id: 2, titel: "JavaScript von vorn", jahr: 2023 },
{ id: 3, titel: "HTTP verstehen", jahr: 2024 },
];
app.get("/buecher", (req, res) => {
let gefunden = buecher;
// req.query.jahr ist Text, auch wenn dort eine Zahl steht. Dieselbe
// Regel wie bei req.params in Lektion 9.3.
if (req.query.jahr !== undefined) {
gefunden = gefunden.filter((buch) => buch.jahr === Number(req.query.jahr));
}
res.json({ anzahl: gefunden.length, daten: gefunden.map((buch) => buch.titel) });
});
app.listen(3000); Rechts steht ?jahr=2024. Ändere die Zahl in der Adresszeile auf 1900 und lass den Parameter
einmal ganz weg, dann hast du alle drei Fälle.
Drei Dinge sind hier wichtig. Der Wert ist Text, auch wenn eine Zahl darin steht: req.query.jahr
ist "2024" und nicht 2024. Ohne Number() vergleichst du eine Zeichenkette mit einer Zahl, und
das ist nie wahr.
Der Filter ist freiwillig. Ohne ?jahr= kommt die ganze Liste, und deshalb wird der Parameter
mit !== undefined geprüft und nicht einfach benutzt. Eine Route, die ohne Parameter gar nichts
liefert, hat den Filter zur Pflicht gemacht, ohne es zu sagen.
Und anzahl zählt das Ergebnis, nicht den Bestand. Wer filtert und trotzdem die Gesamtzahl
meldet, liefert eine Zahl, die zur mitgelieferten Liste nicht passt. Das fällt niemandem sofort auf
und kostet später eine Stunde Suche.
Ein Filter, der nichts findet
Die letzte Anfrage des Beispiels ist der Fall, an dem sich der ganze Artikel schließt: ?jahr=1900
findet nichts und antwortet trotzdem mit 200. Es ist dieselbe Sammlung wie vorher, nur ist nach
dem Filtern nichts übrig. Der 404 bleibt dem Einzelstück vorbehalten.
Zum Mitnehmen
Eine leere Sammlung ist 200 mit leerer Liste, ein fehlendes Einzelstück ist 404. Die Antwortform entscheidest du einmal und hältst sie dann durch.
Jetzt du
Basis Konto, kostenlosZu 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.