Start / Seminare / Next.js 16 Full-Stack-Entwicklung mit React und TypeScript

Modul

Datenbankzugriff mit PostgreSQL

6 Kapitel in diesem Modul-Video · Laufzeit

Vertonung mit synthetischer Stimme · Inhalte redaktionell verantwortet · © 2026 HECKER CONSULTING, alle Rechte vorbehalten

Transkript

Der gesprochene Text dieses Moduls zum Mitlesen, Überfliegen und Durchsuchen. Ein Klick auf einen Zeitstempel springt an die Stelle im Video.

Datenbankzugriff mit PostgreSQL

0:00 Jetzt kommen wir zu den Daten selbst — und zu der Struktur, die ich seit Modul eins ankündige. Dieses Modul hat zwei Gesichter. Das eine ist handwerklich: Datenmodell, typisierter Zugriff, Verbindungen, Migrationen. Das andere ist architektonisch, und es ist das wichtigere. Wir bauen den Datenzugriffs-Layer — einen einzigen Ort, an dem Abfrage, Berechtigung und Zuschnitt zusammenkommen.

0:23 Diese eine Entscheidung trägt anschließend die gesamte Sicherheit in Modul elf und einen guten Teil der Tests in Modul zwölf. Wenn Sie danach das Gefühl haben, für ein Datenbankmodul erstaunlich viel über Sicherheit gehört zu haben, dann war das Absicht.

Datenmodell und Schema

0:39 Fangen wir bei der Domäne an. Wir modellieren die Marina — Liegeplätze, Gäste, Buchungen, Rechnungen. Das ist überschaubar und trägt trotzdem alles, was wir im Seminar brauchen. Zwei Entscheidungen möchte ich dabei ausdrücklich benennen, weil sie sich später kaum zurücknehmen lassen. Beide betreffen Dinge, die man gern in die Anwendung schiebt und die in Wahrheit tiefer gehören.

1:02 Vier Entitäten, und jede hat ihre Rolle. Liegeplätze mit Abmessungen und Tarif, Gäste mit Boot und Rolle, Buchungen mit Zeitraum und Status, Rechnungen mit Positionen. Der Schnitt entscheidet über jede spätere Abfrage — was hier zusammengehört, lässt sich leicht abfragen; was auseinanderliegt, braucht eine Verknüpfung. Zwei Entscheidungen nehmen wir bewusst und nicht nebenbei, weil sie später teuer werden. Es geht um Überschneidungen von Buchungen und um Geldbeträge.

1:30 Beides sieht harmlos aus und ist es nicht. Vier Zeilen für ein ganzes Seminar — das ist Absicht. Ein Modell wird nicht dadurch gut, dass es vollständig ist, sondern dadurch, dass man es im Kopf behalten kann. Wenn wir in Modul elf über Berechtigungen sprechen, müssen Sie sofort wissen, wem eine Buchung gehört, ohne nachzuschlagen.

1:50 Interessant ist die Beziehung zwischen den Zeilen: Die Buchung ist der Dreh- und Angelpunkt, sie verbindet Liegeplatz und Gast und erzeugt die Rechnung. Solche zentralen Entitäten gibt es in fast jedem Fachmodell — sie zu erkennen ist der halbe Entwurf. Der erste Punkt ist der, der in Reviews am meisten Diskussion auslöst.

2:10 Überschneidungsfreiheit von Buchungen wirkt wie Anwendungslogik: Man prüft vor dem Anlegen, ob der Zeitraum frei ist. Und genau diese Prüfung ist wertlos, wenn zwei Anfragen gleichzeitig kommen — beide prüfen, beide sehen frei, beide legen an. Das ist kein theoretischer Fall, das passiert bei jedem beliebten Liegeplatz im Frühjahr. Nur die Datenbank kann das zuverlässig verhindern.

2:33 Der zweite Punkt ist alt und immer noch aktuell: Geldbeträge sind keine Fließkommazahlen. Die Rundungsfehler summieren sich, und irgendwann fehlen zwei Cent. Der zweite Punkt kostet in einem Hafenprojekt garantiert Zeit: Zeitzonen gehören geklärt, bevor die erste Buchung entsteht. Ein Liegeplatz ist von Freitag bis Sonntag gebucht — aber ab wann genau, in welcher Zeitzone, und was passiert bei der Zeitumstellung?

2:58 Wer das später klärt, muss Bestandsdaten korrigieren. Der dritte Punkt ist eine Kleinigkeit mit großer Wirkung: Ein Status als freie Zeichenkette wird binnen Wochen inkonsistent. Irgendwann steht in der Datenbank storniert, Storniert und cancelled nebeneinander, und jede Auswertung ist falsch.

Typisierter Zugriff mit einem ORM

3:16 Jetzt zur Frage, mit der man Abende füllen kann: Wie greifen wir auf die Datenbank zu? Ich mache es kurz und vor allem ohne Glaubensbekenntnis. Es gibt zwei ernsthafte Kandidaten, sie lösen dieselbe Aufgabe auf verschiedenen Wegen, und beide sind gute Antworten. Wichtiger als die Wahl sind die Kriterien, nach denen Sie sie treffen.

3:36 Die beiden Kandidaten heißen Prisma und Drizzle und unterscheiden sich in einer Grundhaltung. Prisma bringt eine eigene Schemasprache mit und erzeugt daraus einen Zugriffsclient — Sie beschreiben Ihr Modell und bekommen Werkzeug. Drizzle bleibt nah an SQL und nutzt TypeScript selbst als Schemaquelle. Beide liefern Typsicherheit bis in die Komponente hinein, und das ist der eigentliche Gewinn: Wenn Sie eine Spalte umbenennen, sagt Ihnen das die Typprüfung, statt dass es zur Laufzeit fehlschlägt.

4:04 Welchen Weg Sie nehmen, hängt davon ab, wie nah Sie an SQL sein wollen. Die Empfehlung unter der Tabelle ist meine ehrliche Einschätzung: Wer SQL kann, gewinnt mit Drizzle, weil er sein Wissen weiterverwendet und jederzeit sieht, was tatsächlich abgesetzt wird. Wer schnell starten will, gewinnt mit Prisma, weil es mehr Entscheidungen abnimmt.

4:24 Die Zeile mit dem Generierungsschritt ist im Alltag relevanter, als sie klingt — ein zusätzlicher Schritt, der nach jeder Schemaänderung laufen muss, wird irgendwann von jemandem vergessen. Und die Bundle-Größe zählt in dem Maß, wie Sie den Zugriff in serverlosen Umgebungen betreiben. Achten Sie auf die erste Zeile — sie ist die aus Modul vier, die dieses Modul im Browser unmöglich macht. Und achten Sie auf das, was nicht dasteht: kein expliziter Rückgabetyp.

4:51 Der ergibt sich aus dem Schema, und wenn Sie eine Spalte ändern, wandert das bis in die Komponente durch. Worauf es ankommt: Das ist noch nicht der fertige Zugriffs-Layer. Es fehlt die Prüfung, es fehlt der Zuschnitt. Wir bauen ihn in Kapitel vier vollständig — dies hier ist die technische Grundlage darunter. Der zweite Punkt ist der Klassiker, der jedem einmal passiert: Beziehungen naiv zu laden erzeugt für jede Zeile eine zusätzliche Abfrage.

5:18 Bei zehn Buchungen merkt das niemand, bei zehntausend steht die Anwendung. Das Tückische ist, dass der Code völlig harmlos aussieht. Der erste Punkt ist die Verallgemeinerung davon: Ein solches Werkzeug ersetzt kein Verständnis der erzeugten Abfragen. Sehen Sie sich zumindest einmal an, was tatsächlich an die Datenbank geht — die meisten Werkzeuge können das protokollieren.

Verbindungen in serverlosen Umgebungen

5:40 Jetzt ein Thema, das rein betrieblich ist und trotzdem Ihre Architektur beeinflusst. Es geht um Datenbankverbindungen und darum, dass moderne Betriebsmodelle mit ihnen anders umgehen, als Datenbanken es erwarten. Wer das übersieht, baut eine Anwendung, die im Test tadellos läuft und unter Last zusammenbricht. Jede Serverinstanz baut ihre eigenen Verbindungen auf. Das war unproblematisch, solange ein Server lief.

6:05 Auf serverlosen Plattformen entstehen und vergehen Instanzen aber ständig — und jede davon möchte Verbindungen. PostgreSQL erlaubt standardmäßig einige Dutzend gleichzeitig. Sie sehen, wohin das führt. Ein Verbindungspool vor der Datenbank ist deshalb kein Feintuning für später, sondern Voraussetzung. Denken Sie an eine Telefonzentrale: Nicht jeder Anrufer bekommt eine eigene Leitung, sondern die Zentrale verteilt die vorhandenen.

6:33 Der Unterschied zwischen den Spalten ist die Lebensdauer. Links viele kurzlebige Instanzen, rechts wenige langlebige. Und daraus folgt alles Weitere: Links brauchen Sie einen Pool außerhalb Ihrer Anwendung, rechts genügt einer im Prozess. Der Satz unter der Tabelle ist mir wichtig, weil er das Modul verbindet — diese Entscheidung fällt eigentlich in Modul dreizehn, beim Deployment, und wirkt bis hierher zurück.

6:56 Wenn Sie noch nicht wissen, wie betrieben wird, dann ist das die erste Frage, die Sie klären sollten. Sie beeinflusst mehr als nur den Zugriff. Der zweite Punkt beschreibt, warum dieser Fehler so lange überlebt: Er tritt erst unter Last auf. In der Entwicklung gibt es eine Instanz und einen Nutzer, da ist alles in Ordnung.

7:16 Beim ersten echten Ansturm kommen die Fehlermeldungen über zu viele Verbindungen — und zwar gehäuft und plötzlich. Der dritte Punkt räumt ein verbreitetes Missverständnis aus: Ein Pool innerhalb Ihres Anwendungsprozesses hilft im serverlosen Betrieb nicht. Er muss davor liegen, als eigener Dienst, damit er über die Instanzen hinweg wirkt.

Der Datenzugriffs-Layer

7:35 Jetzt kommt das Kapitel, auf das ich seit Modul eins hinarbeite. Es geht um einen Ort — einen einzigen — an dem Abfrage, Berechtigung und Zuschnitt zusammenkommen. Diese Struktur ist die zentrale Architekturentscheidung des ganzen Seminars, und sie ist keine Erfindung von mir: Next.js empfiehlt sie für neue Projekte ausdrücklich.

7:55 Sehen wir uns an, warum. Der Zugriffs-Layer ist ein serverseitiges Modul, das jede Abfrage bündelt, dabei die Berechtigung prüft und nur schmale Übertragungsobjekte zurückgibt. Und nur dieser Layer liest Umgebungsvariablen. Stellen Sie sich eine Registratur vor: Sie gehen nicht selbst ins Archiv, Sie stellen einen Antrag am Schalter.

8:15 Dort wird geprüft, ob Sie etwas sehen dürfen, und Sie bekommen eine Kopie mit genau den Angaben, die für Sie bestimmt sind. Der entscheidende Teil ist nicht die Bequemlichkeit — es ist, dass es keinen zweiten Zugang gibt. Der rote Faden dieser fünf Schritte ist Bündelung. Schritt eins macht das Modul serverseitig unverrückbar, Schritt zwei nutzt die Merkfunktion aus Modul fünf, damit die Prüfung billig bleibt.

8:40 Schritt drei ist das Herzstück: In jeder Abfrage wird geprüft, bevor Daten fließen — nicht in der Seite, nicht in der Aktion, hier. Schritt vier begrenzt, was hinausgeht. Und Schritt fünf zieht die Grenze nach außen: Datenbankzugriff und Geheimnisse gibt es nur hier. Danach ist die Frage nach der Sicherheit an einem Ort beantwortbar — und das ist der eigentliche Gewinn.

9:02 Sehen Sie sich die Reihenfolge an: Sitzung holen, Daten holen, Recht prüfen, zugeschnitten zurückgeben. Vier Schritte, und keiner davon ist verhandelbar. Was zurückkommt, sind Nummer und Betrag — nicht die Datenbankzeile mit ihren internen Feldern. Worauf es ankommt: Diese Funktion ist von überall aufrufbar, aus der Seite, aus der Aktion, aus einem Endpunkt.

9:24 Und weil die Prüfung darin steckt, ist sie an allen drei Orten wirksam, ohne dass jemand daran denken muss. Genau das unterscheidet diese Struktur von einer Sammlung von Hilfsfunktionen. Drei Gewinne, und jeder für sich würde den Aufwand rechtfertigen. Die Sicherheit liegt an einem Ort statt über Seiten verstreut. Die Rückgabe ist schmal, also kann nichts versehentlich zum Client wandern — das ist Modul vier, hier eingebaut.

9:49 Und die Logik ist testbar, ohne eine Komponente zu rendern, was in Modul zwölf wichtig wird, weil asynchrone Server-Komponenten sich schlecht testen lassen. Der vierte Punkt ist der für den Betrieb: Ein Audit hat eine klare Frage — greift jemand an diesem Layer vorbei? Die lässt sich mit einer Suche beantworten.

Abfragen und Caching zusammenbringen

10:08 Jetzt bringen wir zwei Module zusammen, und dabei entsteht eine Falle, die ich für die gefährlichste des ganzen Seminars halte. Es geht darum, welche Abfragen gecacht werden dürfen und welche nicht. Der Unterschied ist leicht zu übersehen, und der Fehler zeigt sich niemals beim Testen allein. Die Regel lautet: Eine Abfrage darf gecacht werden, solange sie nicht benutzerabhängig ist. Die Tarifliste ist für alle gleich, also gern.

10:33 Die Rechnungen eines Gastes sind es nicht — die bleiben ungecacht oder wandern in den privaten Cache, der nur im Browser dieses einen Nutzers liegt. Formulieren Sie die Frage am besten so: Wenn zwei verschiedene Menschen diese Funktion aufrufen, bekommen sie dasselbe Ergebnis? Wenn ja, cachen. Wenn nein, ist ein geteilter Cache der falsche Ort — und zwar aus Gründen, die weit über Aktualität hinausgehen.

10:58 Die Frage unter der Tabelle ist die Entscheidungsregel, und sie ist bewusst so formuliert: Sähen zwei Nutzer dasselbe Ergebnis? Die oberen beiden Zeilen sind unkritisch. Die dritte zeigt den Mittelweg — eigene Buchungen dürfen gecacht werden, aber nur privat, also im Browser des Nutzers. Und die vierte ist ein klares Nein.

11:17 Ein Hinweis zum Mittelweg: Der private Cache ist ein starkes Werkzeug, weil er personalisierte Inhalte in das Vorabladen einbezieht. Er ist aber eben auch für den Nutzer selbst lesbar — also nichts hineinlegen, was er nicht sehen dürfte. Hier ist die Falle, die ich angekündigt habe. Wenn Sie eine Abfrage cachen, die die Sitzung liest, dann steht im Cache das Ergebnis für den ersten Nutzer — und der zweite bekommt es ausgeliefert.

11:42 Er sieht die Rechnungen eines Fremden. Das ist ein meldepflichtiger Vorfall, und der Weg dorthin ist eine einzige unbedachte Zeile. Der zweite Punkt erklärt, warum das durchrutscht: Beim Testen allein sind Sie immer derselbe Nutzer. Der dritte nennt den sauberen Ausweg — die Kennung als Argument übergeben, dann wird sie Teil des Schlüssels.

Migrationen und Testdaten

12:03 Zum Abschluss zwei Dinge, die in Übungsprojekten fehlen und in echten Projekten den Unterschied machen. Beide sind unspektakulär, beide werden gern aufgeschoben, und beide entscheiden darüber, ob eine Anwendung tatsächlich betreibbar ist. Wer sie hat, kann ausrollen. Wer sie nicht hat, bastelt. Schemaänderungen laufen über versionierte Migrationen, die im Repository liegen und im Deployment ausgeführt werden — nicht von Hand auf dem Server.

12:30 Dazu kommt ein Skript, das die Entwicklungsdatenbank mit realistischen Hafendaten füllt. Beides zusammen ergibt eine Eigenschaft, die man erst schätzt, wenn man sie hat: Der Zustand der Datenbank ist reproduzierbar. Jeder im Team hat denselben Stand, jede Umgebung lässt sich neu aufbauen, und niemand muss fragen, welche Änderung wo schon gelaufen ist.

12:51 Der erste Punkt wird regelmäßig unterschätzt: Eine leere Datenbank versteckt Fehler. Listen, die nie umbrechen, Sortierungen, die man nie sieht, Namen, die nie zu lang sind. Sie bemerken diese Dinge beim ersten echten Nutzer. Der zweite Punkt ist die Leistungsfrage — bei drei Zeilen ist jede Abfrage schnell, auch die mit der zusätzlichen Abfrage je Zeile aus Kapitel zwei.

13:13 Und der vierte ist der für das nächste Modul: End-to-End-Tests brauchen einen definierten Ausgangszustand. Ohne Seed-Skript sind sie nicht verlässlich zu bauen. Der rote Faden hier ist eine Frage, die Schritt drei stellt: Läuft die alte Version mit dem neuen Schema noch? Denn während eines Rollouts laufen beide Versionen gleichzeitig — die neue startet, die alte bedient noch Anfragen.

13:36 Wenn Ihre Migration das nicht berücksichtigt, gibt es in diesem Fenster Fehler. Deshalb werden Änderungen in zwei Schritten gemacht: erst hinzufügen, dann umstellen, dann entfernen. Das ist mehr Arbeit als ein einziger Schritt und der Unterschied zwischen einem Rollout mit und ohne Ausfall. Der erste Punkt ist genau das eben Gesagte in seiner konkretesten Form: Eine Spalte im selben Schritt umzubenennen bricht die laufende alte Version.

14:01 Sie sucht eine Spalte, die es nicht mehr gibt. Der dritte Punkt ist einer, den ich ernst meine: Testdaten mit echten Personendaten zu füllen ist ein Datenschutzverstoß — auch dann, wenn es nur die Entwicklungsumgebung ist. Gerade in einem Portal mit Gästen und Rechnungen ist die Versuchung groß, einen Produktionsabzug zu nehmen. Tun Sie es nicht.

14:21 Erfundene Daten sind besser, weil sie auch die Grenzfälle abdecken. Diese Aufgabe ist die wichtigste des Moduls, weil ihr Ergebnis in den nächsten drei Modulen weiterverwendet wird. Sie führen Zugriff, Berechtigung und Zuschnitt an einem Ort zusammen. Der Erfolg ist doppelt beschrieben, und beide Hälften zählen: Eine Buchung ist nur für ihren Gast und die Verwaltung lesbar, und die Rückgabe enthält keine internen Felder.

14:45 Wer schneller ist, ergänzt die Ausschlussbedingung gegen doppelte Belegung — damit hätten Sie den Punkt umgesetzt, über den wir zu Beginn des Moduls gesprochen haben.

Modul 9 zusammengefasst

14:54 Vier Sätze zum Mitnehmen. Fachliche Invarianten gehören in die Datenbank, nicht in die Anwendung. Ein Verbindungspool ist im serverlosen Betrieb Voraussetzung, nicht Feintuning. Der Zugriffs-Layer bündelt Abfrage, Recht und Zuschnitt an einem Ort. Und was die Sitzung liest, darf nicht in einen geteilten Cache. Im nächsten Modul kümmern wir uns darum, wer dieser Nutzer eigentlich ist: Anmeldung und Sitzungsverwaltung.

15:19 Danach, in Modul elf, ziehen wir die Fäden zusammen und sprechen über Berechtigungen — dann zahlt sich der Layer aus, den Sie heute gebaut haben.

Lieber mit Trainer? Dieses Modul ist Teil unserer Team-Schulungen — mit Übungen, Ihrem eigenen Code und Fragen, die ein Video nicht beantwortet. Mehr erfahren →