Im ersten Artikel zum Thema Next.js haben Sie die Grundlagen von Next.js, dem populären Hybrid-Framework auf Basis von React, kennengelernt. Sie haben erfahren, was es mit der Struktur im Dateisystem auf sich hat und wie Sie Ihre Applikation aufbauen können. Seit der Version 13 steht Ihnen mit dem App-Router eine neue Struktur zur Verfügung, die auf den Server Components von React 18 aufbaut. Next nutzt die nativen React-Features damit noch besser aus, um schnellere und dynamischere Applikationen zu generieren. In Teil 2 erfahren Sie nun mehr darüber, wie Sie die verschiedenen Aspekte einer Applikation umsetzen und dabei die Features des Frameworks gewinnbringend einsetzen können.

Als Beispiel implementieren wir eine Marktplatz-Applikation, mit der BenutzerInnen Produkte ansehen und kaufen können. Bisher ist diese Applikation in der Lage, eine Liste von Produkten serverseitig zu erzeugen, Detailansichten für jedes Produkt zu präsentieren und Artikel in den Warenkorb zu legen. Im Laufe dieses Artikels erfahren Sie, wie Sie die Funktionalität dieser Applikation erweitern können, indem Sie sowohl Next-eigene Features nutzen als auch zusätzliche Bibliotheken einbinden.

Next steht wie kaum ein anderes Framework für eine neue Art von React-Applikationen. Ursprünglich war React ein Werkzeug, das sich auf das Erzeugen von User Interfaces fokussierte, also eine rein clientseitige Bibliothek. Über die Möglichkeit, Komponenten serverseitig zu rendern, also Server Side Rendering (SSR) zu betreiben, verfügt React schon lange. Die native Umsetzung ist jedoch wenig komfortabel. An genau diesem Punkt setzt Next an. Eines der wichtigsten Features dieses Frameworks, das auf React aufbaut, ist, dass es Client und Server näher zusammenbringt.

API-Routen - Abbildung 1: Ansicht des Warenkorbs der Beispielapplikation

Ansicht des Warenkorbs der Beispielapplikation

Serverseitiges React: SSR und SSG

Mit ihrem rein clientseitigen Fokus beschränken sich moderne Single Page Application-Frameworks unnötigerweise selbst. Lösungen wie React werden in Form eines nahezu leeren HTML-Dokuments sowie einige JavaScript- und CSS-Dateien über einen statischen Webserver an die Clients ausgeliefert. Der Browser stellt zunächst eine weiße Seite dar und baut anschließend mithilfe von JavaScript die eigentliche Applikation auf.

Serverseitiges React in Form von SSR und SSG (Static Site Generation) löst dieses Problem, indem die angefragten Seiten serverseitig vorgeneriert werden und React im Anschluss im Browser die Kontrolle über diese Struktur übernimmt. Im Fall der SSG hat das den Vorteil, dass der Server die fertigen Inhalte deutlich schneller ausliefert, da sie nicht erst für die aktuelle Anfrage erzeugt werden, sondern bereits im Dateisystem liegen.

SSR arbeitet zwar ebenfalls serverseitig, bietet jedoch einige andere Vorteile. Hier ist die Performance nicht so optimal wie bei SSG, der Client erhält jedoch auch eine vorgenerierte Struktur, was sowohl für Suchmaschinen als auch für tatsächliche BenutzerInnen Vorteile bietet. Zusätzlich dazu kann der Server bei dem dynamischeren SSR Inhalte auf Zuruf erzeugen. So können Sie dafür sorgen, dass gewisse Informationen über den Aufbau von Komponenten vor den BenutzerInnen verborgen bleiben.

Große Auswahl an günstigen Domain-Endungen – schon ab 0,08 € /Monat
Jetzt Domain-Check starten

Static Site Generation in Next

Standardmäßig handelt es sich bei den Komponenten einer modernen Next-Applikation, die mit dem neuen App Router gebaut wird, um Server Komponenten. Diese rendert das Framework serverseitig und bildet damit die Grundlage für SSR und SSG. Für eine optimale Performance geht Next außerdem von statischen Komponenten aus.

Eine solche Komponente kann auch die fetch-Funktion zum Laden von Daten verwenden. Das Ergebnis eines solchen Aufrufs speichert Next in einem Cache zwischen. Sie können dieses Caching-Verhalten deaktivieren, indem Sie die cache-Option auf den Wert „no-store“ setzen.

Das Deaktivieren des Caches ist auch Ihre erste Möglichkeit, um vom statischen zum dynamischen Rendern zu wechseln. Diese Variante des Cachings schließt auch SSG aus und lässt lediglich SSR zu. Nutzen Sie die cookies()– oder headers()-Funktionen in Ihrer Server-Komponente, schaltet Next ebenfalls ins dynamische Rendern. Genauso, wenn Sie die searchParams-Prop nutzen.

Static Site Generation bietet noch eine weitere interessante Möglichkeit. Sie können die statisch gerenderten Page-Komponenten Ihrer Applikation über einen statischen Webserver ausliefern lassen. Das bedeutet, dass Sie nur zum Bauen der Applikation einen Node.js-Prozess benötigen. Zur Laufzeit ist er nicht mehr erforderlich. Damit das funktioniert, müssen Sie in der next.config.js-Datei die output-Eigenschaft mit dem Wert „export“ definieren. Führen Sie dann das Kommando „next build“ aus, erzeugt der Build-Prozess ein Verzeichnis mit dem Namen out, das die statischen Inhalte Ihrer Applikation enthält, die Sie im Document Root eines Webservers wie beispielsweise Nginx oder Apache platzieren können.

Die Ausgabe, die Next für Sie erzeugt, enthält sowohl das statisch gerenderte HTML als auch die statische Payload für die clientseitige Navigation zwischen den Page Komponenten. Damit vermeidet Next vollständige Pageloads zur Laufzeit auch in einer statischen Umgebung.

Formulare in einer Next-Applikation

Die Server-Komponenten von Next sind davon gekennzeichnet, dass sie zwar sehr performant sind und das Potenzial bieten, statisch oder dynamisch serverseitig gerendert werden zu können. Was sie allerdings nicht unterstützen, ist die direkte Interaktion von BenutzerInnen, also die Behandlung von clientseitigen Events wie Klicks oder Eingaben. Außerdem können sie keinen eigenen State oder Lifecycle Effekte verwenden.

Daraus folgt, dass sie auch keine Custom Hooks in Serverkomponenten nutzen können, die solche Strukturen nutzen. Das bedeutet, dass Sie keine hochdynamische Applikation mit reinen Server-Komponenten umsetzen können.

Der große Vorteil von Next ist das nahtlose Zusammenspiel von Client- und Serverkomponenten. Dies macht sich vor allem bei der Umsetzung von Formularen bemerkbar. Hier haben Sie ein hohes Maß an Interaktivität und speichern den Zustand des Formulars meist auch noch im State einer Komponente. In der React-Welt hat sich etabliert, dass Sie in den meisten Fällen eine zusätzliche Bibliothek wie beispielsweise React Hook Form verwenden.

In unserer Beispiel-Applikation starten Sie auf einer Übersichtsliste mit Produkten, die Sie direkt in Ihren Warenkorb legen können. Den Warenkorb verwaltet die Applikation im Frontend in einem React-Context. Hier speichern Sie lediglich die Id des Artikels und die gewünschte Menge. Auf der Listen-Seite führt ein Link direkt zur Warenkorb-Seite, der über den Pfad /cart erreichbar sein soll. Mit dem dateisystembasierten Routing von Next erzeugen Sie diese Route, indem Sie im app-Verzeichnis ein neues Unterverzeichnis mit dem Namen „cart“ und darin eine Datei mit dem Namen page.tsx anlegen. Im Warenkorb zeigen Sie ein Formular an, über das Ihre BenutzerInnen ihre persönlichen Daten wie Namen und Adresse angeben können. Außerdem rendern Sie eine Übersicht über die aktuelle Bestellung. Den Abschluss bildet der „kaufen“-Button, mit dessen Hilfe die Bestellung abgeschickt wird.

Um den Code im Zusammenhang zu zeigen, sehen Sie die CartForm-Komponente in Listing 1 als zusammenhängenden Codeblock. In einer Produktiv-Applikation sollten Sie die einzelnen Teile in separate Komponenten auslagern, die sich dann nur um einen bestimmten Aspekt kümmern.

Die CartForm-Komponente vereinigt eine ganze Reihe von Features einer typischen Next-Applikation in sich. Dabei handelt es sich um eine typische Client-Komponente, da sie die State- und Effect-Hooks verwendet und über das Formular einen direkten Interaktionspunkt mit den BenutzerInnen der Applikation bietet.

Da der Warenkorb in unserem Beispiel vollständig clientseitig vorgehalten wird, greifen Sie zunächst auf den CartContext zu, um die einzelnen Elemente und die gewünschte Anzahl auszulesen. Diese Informationen nutzen Sie in der Callback-Funktion des Effect-Hooks, um die Anzeige des Warenkorbs zu füllen. Dafür senden Sie eine Post-Anfrage an den Pfad /products. Dieser Endpunkt ist, ebenso wie der /checkout-Endpunkt, den Sie zum Speichern der Bestellung verwenden, in Ihrer Next-Applikation implementiert. Dem Thema API-Routen widmen wir uns direkt im Anschluss. Der /products-Endpunkt antwortet mit einem Array von CartItem-Objekten, die Sie direkt zur Darstellung der Bestellübersicht verwenden können. Sie beinhalten die Bezeichnung des Artikels, den Preis, die gewünschte Anzahl sowie den aufsummierten Preis für den Artikel. Die letzte Zeile der Tabelle zeigt die Gesamtsumme der Bestellung. Diese können Sie berechnen, indem Sie die Array.reduce-Methode verwenden und die einzelnen Produktsummen addieren.

Der Kern des Warenkorbs ist das Formular, über das die BenutzerInnen ihre Daten eingeben können. React wie auch Next machen Ihnen keine Vorgaben, wie Sie Ihre Formulare umsetzen. Sie können die Formularbehandlung entweder komplett selbst übernehmen oder auf eine der etablierten Formularbibliotheken zurückgreifen. In unserem Fall ist das die Bibliothek React Hook Form, die das Handling der einzelnen Formularelemente übernimmt und den Zustand des Formulars in einem eigenen internen State speichert. Sowohl wegen des States als auch wegen der Interaktion ist diese Bibliothek sowie ihr Einsatz ein klarer Fall für eine Clientkomponente in Next.

Mit „npm install react-hook-form“ binden Sie die Bibliothek in Ihre Applikation ein. Die „useForm“-Funktion liefert Ihnen alles, was Sie für die Formularimplementierung benötigen. Im Beispiel nutzen Sie die Funktionen register und handleSubmit. React Hook Form unterstützt standardmäßig TypeScript und implementiert die useForm als generische Funktion, der Sie die Datenstruktur des Formulars übergeben können. Mit dieser Information kann Sie Ihre Entwicklungsumgebung bei der Implementierung unterstützen, indem sie Ihnen Syntaxvervollständigung anbietet.

Die Eingabefelder des Formulars verknüpfen Sie mit der register-Funktion mit React Hook Form. Das Absenden des Formulars behandelt die handleSubmit-Methode. Sie verhindert beispielsweise, dass das Standardverhalten des Browsers Anwendung findet und das Formular inklusive Pageload an den Server übermittelt. Die Funktion akzeptiert eine Callback-Funktion, mit der Sie beispielsweise die Daten selbst verarbeiten können.

In der onSubmit-Funktion übernehmen Sie die Daten, die Ihnen React Hook Form übergibt, und senden Sie an die API Route /checkout. Nachdem der Server die Daten verarbeitet hat und Ihnen eine Antwort gesendet hat, leiten Sie den Browser an die /thanks-Route weiter, mit der Sie signalisieren, dass die Bestellung erfolgreich war. An diesem Beispiel sehen Sie, dass Sie mit Next nicht nur mit der Link-Komponente relativ statisch zwischen den Ansichten Ihrer Applikation wechseln können, sondern auch aus dem Programmcode heraus dynamisch navigieren können.

Die Grundlage für diese Art der Navigation bildet die Hook-Funktion useRouter, die Ihnen Next zur Verfügung stellt. Die useRouter-Funktion können Sie nur in Clientkomponenten verwenden. Das Objekt, das Ihnen die Funktion zurückgibt, verfügt über die push-Methode, der Sie den Zielpfad der Navigation übergeben und somit den Wechsel der Ansicht auslösen. Wie in Next üblich führt die Navigation nicht zu einem vollständigen Laden der Seite. Stattdessen tauscht Next nur die Teile der Seite aus, die sich ändern müssen. Alle übrigen Strukturen wie beispielsweise übergeordnete Layouts und auch den Inhalt des Speichers wie ein Context bleiben erhalten.

Den Ablauf, den Sie hier gesehen haben, deckt lediglich den Erfolgsfall ab. In einer echten Applikation müssen Sie auf alle Eventualitäten vorbereitet sein. So kann das Laden der Warenkorbdaten oder das Absenden der Bestellung fehlschlagen. In beiden Fällen müssen Sie Ihre BenutzerInnen über das Problem informieren und im Idealfall eine Lösungsmöglichkeit, beispielsweise in Form eines Retrys, bieten.

Auch die Validierung von Eingaben gehört zum Standard bei der Implementierung. Die clientseitige Validierung ist ein Komfortfeature, das den BenutzerInnen beim Ausfüllen des Formulars hilft, Fehler zu vermeiden. Die serverseitige Validierung ist ein notwendiges Feature, um die Sicherheit von Client und Server sicherzustellen und die Konsistenz der Daten zu wahren. Bei der clientseitigen Validierung bieten Bibliotheken wie React Hook Form Erweiterungen, die Ihnen die Arbeit erheblich erleichtern.

Die Umfänge reichen von einfachen Standard-Validierungen mit Minimal- und Maximallänge, numerische Eingaben oder E-Mail-Muster bis hin zu eigendefinierten- und Schemavalidierungen. Für weitere Informationen zu diesem Thema möchte ich Sie an dieser Stelle auf die Dokumentation von React Hook Form verweisen . Mit dieser Formular-Implementierung wenden wir uns nun dem serverseitigen Gegenstück zu, den API Routen von Next.

API Routen – das “Backend for Frontend” in Next

Wenn Sie Next nicht nur zur Erzeugung von statisch gerenderten HTML-Dokumenten verwenden, haben Sie zur Laufzeit Ihrer Applikation einen Node-Prozess zur Verfügung. Dessen Hauptaufgabe ist das dynamische Rendern von Komponenten und ihre Auslieferung an die Clients. In diesem Prozess haben Sie, je nach Konfiguration, entweder den gesamten Funktionsumfang von Node oder im Fall der Edge-Runtime nur ein reduziertes Featureset. Die Edge-Runtime ist vor allem interessant, wenn es darum geht, Ressourcen zu sparen. Diese Laufzeitumgebung hat einen deutlich geringeren Memory-Footprint als Node.

Was liegt also näher, als dass Sie diesen Prozess nicht nur für das Rendern Ihrer Komponenten, sondern auch für Backend-Aufgaben nutzen? Für diesen Zweck sieht Next die sogenannten API Routen vor. Grundsätzlich folgen sie denselben Prinzipien wie auch die Page-Komponenten und gliedern sich damit nahtlos in Ihren Verzeichnisbaum ein. Statt wie bisher Dateien mit dem Namen page.tsx zu erzeugen, lauten die Dateien der API-Routen route.ts.

In Listing 2 sehen Sie die Implementierung einer solchen API-Route am Beispiel der products-Route, der sie die Ids und Mengen von Produkten in Ihrem Warenkorb schicken und eine um weitere Informationen angereicherte Struktur zurückbekommen.

Haben Sie sich bei der Initialisierung Ihrer Applikation für TypeScript entschieden, nutzen Sie auch für die API Routen TypeScript. Erzeugen Sie in Ihrer Applikation im Verzeichnis “app” ein neues Unterverzeichnis products und dort die Datei route.ts, haben Sie bereits den Pfad festgelegt, also /products. In der route.ts-Datei können Sie dann Funktionen exportieren, deren Name wie der der jeweiligen http-Methode lautet. Möchten Sie auf eine POST-Anfrage reagieren, implementieren Sie die POST-Funktion und exportieren diese.

Next erlaubt, dass Sie diese Funktionen als async-Funktion implementieren und so komfortabel auch asynchrone Abläufe umsetzen können. In den API-Routen arbeitet Next mit Objekten vom Typ NextRequest und NextResponse, die jeweils für die eingehende Anfrage und die ausgehende Antwort stehen. Das request-Objekt erhalten Sie als Parameter der Routing-Funktion und können über dieses auf die verschiedenen Aspekte der Anfrage zugreifen, unter anderem wie im Beispiel auch auf den Body. Zum Erzeugen der Antwort nutzen Sie die json-Methode der NextResponse-Klasse, die die erforderliche Struktur erzeugt, die Sie dann zurückgeben können. Next kümmert sich dann um die Formulierung der Antwort und den Versand an den Browser.

Reicht Ihnen der Standard-Funktionsumfang von Next für die Arbeit mit API-Routen nicht aus, können Sie auf das gesamte Node-Ökosystem zurückgreifen und beispielsweise die Pakete validator oder joi für die Validierung nutzen.

React-Apps mit Next.js bauen – Teil 2: Formulare und API-Routen – Fazit

Next kann zwar als Fullstack-Lösung verwendet werden, seine wirkliche Stärke liegt jedoch eher in der Rolle eines Frontends und einem zugehörigen Backend für Frontends. Next kümmert sich um das serverseitige Rendern von Komponenten und deren Auslieferung, kann aber auch Serverschnittstellen zur Verfügung stellen.

In seiner Rolle eignet sich Next hervorragend in der Kombination mit weiteren Backend-Services oder einem CMS für die Erstellung von öffentlich verfügbaren Applikationen im Internet und muss sich auch im Website- und E-Commerce-Bereich nicht verstecken.

Sebastian Springer

Große Auswahl an günstigen Domain-Endungen – schon ab 0,08 € /Monat
Jetzt Domain-Check starten