Datenmutierung
Im vorherigen Kapitel haben Sie Such- und Paginierungsfunktionen mit URL-Suchparametern und Next.js-APIs implementiert. Lassen Sie uns weiter an der Rechnungsseite arbeiten, indem wir die Möglichkeit hinzufügen, Rechnungen zu erstellen, zu aktualisieren und zu löschen!
Was sind Server Actions?
React Server Actions ermöglichen es Ihnen, asynchronen Code direkt auf dem Server auszuführen. Sie eliminieren die Notwendigkeit, API-Endpunkte zur Datenmutierung zu erstellen. Stattdessen schreiben Sie asynchrone Funktionen, die auf dem Server ausgeführt werden und von Ihren Client- oder Server-Komponenten aufgerufen werden können.
Sicherheit hat oberste Priorität für Webanwendungen, da sie anfällig für verschiedene Bedrohungen sein können. Hier kommen Server Actions ins Spiel. Sie beinhalten Funktionen wie verschlüsselte Closures, strikte Eingabeüberprüfungen, Hashung von Fehlermeldungen, Host-Einschränkungen und mehr – all das trägt dazu bei, die Sicherheit Ihrer Anwendung erheblich zu verbessern.
Verwendung von Formularen mit Server Actions
In React können Sie das action
-Attribut im <form>
-Element verwenden, um Aktionen aufzurufen. Die Aktion erhält automatisch das native FormData-Objekt, das die erfassten Daten enthält.
Beispiel:
Ein Vorteil des Aufrufs einer Server Action innerhalb einer Server-Komponente ist das progressive Enhancement – Formulare funktionieren auch dann, wenn JavaScript auf dem Client noch nicht geladen wurde, z.B. bei langsamen Internetverbindungen.
Next.js mit Server Actions
Server Actions sind auch tief in das Next.js-Caching integriert. Wenn ein Formular über eine Server Action abgeschickt wird, können Sie nicht nur die Aktion zur Datenmutierung verwenden, sondern auch den zugehörigen Cache mit APIs wie revalidatePath
und revalidateTag
neu validieren.
Lassen Sie uns sehen, wie das alles zusammen funktioniert!
Erstellen einer Rechnung
Hier sind die Schritte, die Sie unternehmen werden, um eine neue Rechnung zu erstellen:
- Erstellen Sie ein Formular, um die Benutzereingaben zu erfassen.
- Erstellen Sie eine Server Action und rufen Sie sie aus dem Formular auf.
- Extrahieren Sie in Ihrer Server Action die Daten aus dem
formData
-Objekt. - Validieren und bereiten Sie die Daten für die Einfügung in Ihre Datenbank vor.
- Fügen Sie die Daten ein und behandeln Sie etwaige Fehler.
- Validieren Sie den Cache neu und leiten Sie den Benutzer zurück zur Rechnungsseite.
1. Erstellen Sie eine neue Route und ein Formular
Erstellen Sie zunächst innerhalb des Ordners /invoices
ein neues Routensegment namens /create
mit einer page.tsx
-Datei:

Sie werden diese Route verwenden, um neue Rechnungen zu erstellen. Fügen Sie den folgenden Code in Ihre page.tsx
-Datei ein und studieren Sie ihn:
Ihre Seite ist eine Server-Komponente, die customers
abruft und an die <Form>
-Komponente übergibt. Um Zeit zu sparen, haben wir die <Form>
-Komponente bereits für Sie erstellt.
Navigieren Sie zur <Form>
-Komponente, und Sie werden sehen, dass das Formular:
- Ein
<select>
-Element (Dropdown) mit einer Liste von Kunden enthält. - Ein
<input>
-Element für den Betrag mittype="number"
enthält. - Zwei
<input>
-Elemente für den Status mittype="radio"
enthält. - Einen Button mit
type="submit"
enthält.
Unter http://localhost:3000/dashboard/invoices/create sollten Sie folgende Benutzeroberfläche sehen:

2. Erstellen Sie eine Server Action
Gut, jetzt erstellen wir eine Server Action, die aufgerufen wird, wenn das Formular abgeschickt wird.
Navigieren Sie zu Ihrem lib/
-Verzeichnis und erstellen Sie eine neue Datei namens actions.ts
. Fügen Sie am Anfang dieser Datei die React-use server
-Direktive hinzu:
Durch das Hinzufügen von 'use server'
markieren Sie alle exportierten Funktionen innerhalb der Datei als Server Actions. Diese Server-Funktionen können dann in Client- und Server-Komponenten importiert und verwendet werden. Alle Funktionen in dieser Datei, die nicht verwendet werden, werden automatisch aus dem endgültigen Anwendungsbundle entfernt.
Sie können Server Actions auch direkt in Server-Komponenten schreiben, indem Sie "use server"
innerhalb der Aktion hinzufügen. Für diesen Kurs werden wir sie jedoch in einer separaten Datei organisieren. Wir empfehlen, eine separate Datei für Ihre Aktionen zu verwenden.
Erstellen Sie in Ihrer actions.ts
-Datei eine neue asynchrone Funktion, die formData
akzeptiert:
Importieren Sie dann in Ihrer <Form>
-Komponente createInvoice
aus Ihrer actions.ts
-Datei. Fügen Sie dem <form>
-Element ein action
-Attribut hinzu und rufen Sie die createInvoice
-Aktion auf.
Gut zu wissen: In HTML würden Sie eine URL an das
action
-Attribut übergeben. Diese URL wäre das Ziel, an das Ihre Formulardaten gesendet werden sollten (normalerweise ein API-Endpunkt).In React wird das
action
-Attribut jedoch als spezielles Prop betrachtet – React baut darauf auf, um das Aufrufen von Aktionen zu ermöglichen.Hinter den Kulissen erstellen Server Actions einen
POST
-API-Endpunkt. Deshalb müssen Sie keine API-Endpunkte manuell erstellen, wenn Sie Server Actions verwenden.
3. Extrahieren Sie die Daten aus formData
Zurück in Ihrer actions.ts
-Datei müssen Sie die Werte von formData
extrahieren. Dafür gibt es einige Methoden. Für dieses Beispiel verwenden wir die .get(name)
-Methode.
Tipp: Wenn Sie mit Formularen arbeiten, die viele Felder haben, sollten Sie die
entries()
-Methode mit JavaScriptsObject.fromEntries()
in Betracht ziehen.
Um zu überprüfen, ob alles korrekt verbunden ist, testen Sie das Formular. Nach dem Absenden sollten Sie die Daten, die Sie gerade eingegeben haben, in Ihrem Terminal (nicht im Browser) protokolliert sehen.
Jetzt, da Ihre Daten in Form eines Objekts vorliegen, wird es viel einfacher sein, damit zu arbeiten.
4. Validieren und bereiten Sie die Daten vor
Bevor Sie die Formulardaten an Ihre Datenbank senden, sollten Sie sicherstellen, dass sie im richtigen Format und mit den richtigen Typen vorliegen. Wenn Sie sich an den Anfang des Kurses erinnern, erwartet Ihre Rechnungstabelle Daten im folgenden Format:
Bisher haben Sie nur die customer_id
, den amount
und den status
aus dem Formular.
Typvalidierung und -umwandlung
Es ist wichtig zu validieren, dass die Daten aus Ihrem Formular mit den erwarteten Typen in Ihrer Datenbank übereinstimmen. Wenn Sie beispielsweise ein console.log
in Ihre Aktion einfügen:
Werden Sie feststellen, dass amount
vom Typ string
und nicht number
ist. Das liegt daran, dass input
-Elemente mit type="number"
tatsächlich einen String und keine Zahl zurückgeben!
Für die Typvalidierung haben Sie mehrere Möglichkeiten. Während Sie Typen manuell validieren können, kann eine Typvalidierungsbibliothek Ihnen Zeit und Mühe sparen. Für Ihr Beispiel verwenden wir Zod, eine TypeScript-first-Validierungsbibliothek, die diese Aufgabe vereinfachen kann.
Importieren Sie Zod in Ihrer actions.ts
-Datei und definieren Sie ein Schema, das der Struktur Ihres Formularobjekts entspricht. Dieses Schema validiert die formData
, bevor sie in einer Datenbank gespeichert werden.
Das amount
-Feld ist speziell darauf ausgelegt, von einem String in eine Zahl umgewandelt zu werden, während gleichzeitig der Typ validiert wird.
Sie können dann Ihre rawFormData
an CreateInvoice
übergeben, um die Typen zu validieren:
Speichern von Werten in Cent
Es ist in der Regel eine gute Praxis, Geldbeträge in Cent in Ihrer Datenbank zu speichern, um JavaScript-Gleitkommafehler zu vermeiden und eine höhere Genauigkeit zu gewährleisten.
Lassen Sie uns den Betrag in Cent umrechnen:
Erstellen neuer Datumsangaben
Erstellen wir schließlich ein neues Datum mit dem Format "YYYY-MM-DD" für das Erstellungsdatum der Rechnung:
5. Einfügen der Daten in Ihre Datenbank
Jetzt, da Sie alle Werte für Ihre Datenbank haben, können Sie eine SQL-Abfrage erstellen, um die neue Rechnung in Ihre Datenbank einzufügen und die Variablen zu übergeben:
Im Moment behandeln wir keine Fehler. Darüber werden wir im nächsten Kapitel sprechen. Lassen Sie uns zunächst mit dem nächsten Schritt fortfahren.
6. Revalidierung und Weiterleitung
Next.js verfügt über einen Client-seitigen Router-Cache, der die Routensegmente für eine gewisse Zeit im Browser des Nutzers speichert. Zusammen mit Prefetching stellt dieser Cache sicher, dass Nutzer schnell zwischen Routen navigieren können, während gleichzeitig die Anzahl der Anfragen an den Server reduziert wird.
Da Sie die in der Rechnungsroute angezeigten Daten aktualisieren, möchten Sie diesen Cache leeren und eine neue Anfrage an den Server auslösen. Dies können Sie mit der revalidatePath
-Funktion von Next.js erreichen:
Sobald die Datenbank aktualisiert wurde, wird der Pfad /dashboard/invoices
revalidiert und frische Daten werden vom Server abgerufen.
An diesem Punkt möchten Sie den Nutzer auch zurück zur /dashboard/invoices
-Seite weiterleiten. Dies können Sie mit der redirect
-Funktion von Next.js tun:
Glückwunsch! Sie haben gerade Ihre erste Server-Aktion implementiert. Testen Sie sie, indem Sie eine neue Rechnung hinzufügen. Wenn alles korrekt funktioniert:
- Sie sollten nach dem Absenden zur
/dashboard/invoices
-Route weitergeleitet werden. - Sie sollten die neue Rechnung oben in der Tabelle sehen.
Rechnung aktualisieren
Das Formular zur Rechnungsaktualisierung ähnelt dem Formular zur Rechnungserstellung, mit dem Unterschied, dass Sie die Rechnungs-id
übergeben müssen, um den Datensatz in Ihrer Datenbank zu aktualisieren. Sehen wir uns an, wie Sie die Rechnungs-id
erhalten und übergeben können.
Diese Schritte führen Sie durch, um eine Rechnung zu aktualisieren:
- Erstellen Sie ein neues dynamisches Routensegment mit der Rechnungs-
id
. - Lesen Sie die Rechnungs-
id
aus den Seiten-Parametern. - Holen Sie die spezifische Rechnung aus Ihrer Datenbank.
- Füllen Sie das Formular mit den Rechnungsdaten vor.
- Aktualisieren Sie die Rechnungsdaten in Ihrer Datenbank.
1. Dynamisches Routensegment mit der Rechnungs-id
erstellen
Next.js ermöglicht es Ihnen, Dynamische Routensegmente zu erstellen, wenn Sie den genauen Segmentnamen nicht kennen und Routen basierend auf Daten erstellen möchten. Dies könnte Blogpost-Titel, Produktseiten usw. sein. Sie können dynamische Routensegmente erstellen, indem Sie einen Ordnernamen in eckige Klammern setzen. Zum Beispiel [id]
, [post]
oder [slug]
.
Erstellen Sie in Ihrem /invoices
-Ordner ein neues dynamisches Route namens [id]
, dann eine neue Route namens edit
mit einer page.tsx
-Datei. Ihre Dateistruktur sollte so aussehen:
![Invoices-Ordner mit einem verschachtelten [id]-Ordner und einem edit-Ordner darin](https://h8DxKfmAPhn8O0p3.public.blob.vercel-storage.com/learn/light/edit-invoice-route.png)
In Ihrer <Table>
-Komponente fällt auf, dass es einen <UpdateInvoice />
-Button gibt, der die id
der Rechnung aus den Tabellendatensätzen erhält.
Navigieren Sie zu Ihrer <UpdateInvoice />
-Komponente und aktualisieren Sie den href
des Link
, um die id
-Prop zu akzeptieren. Sie können Template-Literale verwenden, um auf ein dynamisches Routensegment zu verlinken:
2. Rechnungs-id
aus den Seiten-params
lesen
Fügen Sie in Ihrer <Page>
-Komponente den folgenden Code ein:
Beachten Sie, dass es Ihrer /create
-Rechnungsseite ähnelt, außer dass es ein anderes Formular importiert (aus der edit-form.tsx
-Datei). Dieses Formular sollte mit einem defaultValue
für den Kundennamen, den Rechnungsbetrag und den Status vorausgefüllt sein. Um die Formularfelder vorauszufüllen, müssen Sie die spezifische Rechnung mit id
abrufen.
Zusätzlich zu searchParams
akzeptieren Page-Komponenten auch eine Prop namens params
, die Sie verwenden können, um auf die id
zuzugreifen. Aktualisieren Sie Ihre <Page>
-Komponente, um die Prop zu empfangen:
3. Spezifische Rechnung abrufen
Dann:
- Importieren Sie eine neue Funktion namens
fetchInvoiceById
und übergeben Sie dieid
als Argument. - Importieren Sie
fetchCustomers
, um die Kundennamen für das Dropdown-Menü abzurufen.
Sie können Promise.all
verwenden, um sowohl die Rechnung als auch die Kunden parallel abzurufen:
Sie werden einen temporären TypeScript-Fehler für die invoice
-Prop in Ihrem Terminal sehen, weil invoice
potenziell undefined
sein könnte. Machen Sie sich darüber vorerst keine Sorgen, Sie werden dies im nächsten Kapitel beheben, wenn Sie die Fehlerbehandlung hinzufügen.
Großartig! Testen Sie nun, ob alles korrekt verbunden ist. Besuchen Sie http://localhost:3000/dashboard/invoices und klicken Sie auf das Stiftsymbol, um eine Rechnung zu bearbeiten. Nach der Navigation sollten Sie ein Formular sehen, das mit den Rechnungsdetails vorausgefüllt ist:

Die URL sollte auch mit einer id
aktualisiert werden, wie folgt: http://localhost:3000/dashboard/invoice/uuid/edit
UUIDs vs. Auto-incrementing Keys
Wir verwenden UUIDs anstelle von inkrementierenden Schlüsseln (z.B. 1, 2, 3 usw.). Dadurch wird die URL länger; jedoch eliminieren UUIDs das Risiko von ID-Kollisionen, sind global eindeutig und reduzieren das Risiko von Enumeration-Angriffen – was sie ideal für große Datenbanken macht.
Wenn Sie jedoch sauberere URLs bevorzugen, könnten Sie auto-incrementing Keys bevorzugen.
4. id
an die Server-Aktion übergeben
Schließlich möchten Sie die id
an die Server-Aktion übergeben, damit Sie den richtigen Datensatz in Ihrer Datenbank aktualisieren können. Sie können die id
nicht wie folgt als Argument übergeben:
Stattdessen können Sie die id
mit JS bind
an die Server-Aktion übergeben. Dies stellt sicher, dass alle an die Server-Aktion übergebenen Werte kodiert sind.
Hinweis: Die Verwendung eines versteckten Eingabefelds in Ihrem Formular funktioniert ebenfalls (z.B.
<input type="hidden" name="id" value={invoice.id} />
). Allerdings werden die Werte im HTML-Quelltext als Klartext erscheinen, was für sensible Daten nicht ideal ist.
Erstellen Sie dann in Ihrer actions.ts
-Datei eine neue Aktion namens updateInvoice
:
Ähnlich wie bei der createInvoice
-Aktion:
- Extrahieren Sie die Daten aus
formData
. - Validieren Sie die Typen mit Zod.
- Konvertieren Sie den Betrag in Cent.
- Übergeben Sie die Variablen an Ihre SQL-Abfrage.
- Rufen Sie
revalidatePath
auf, um den Client-Cache zu leeren und eine neue Server-Anfrage auszulösen. - Rufen Sie
redirect
auf, um den Nutzer zur Rechnungsseite weiterzuleiten.
Testen Sie es, indem Sie eine Rechnung bearbeiten. Nach dem Absenden des Formulars sollten Sie zur Rechnungsseite weitergeleitet werden, und die Rechnung sollte aktualisiert sein.
Rechnung löschen
Um eine Rechnung mit einer Server-Aktion zu löschen, umschließen Sie den Lösch-Button mit einem <form>
-Element und übergeben Sie die id
mit bind
an die Server-Aktion:
Erstellen Sie in Ihrer actions.ts
-Datei eine neue Aktion namens deleteInvoice
.
Da diese Aktion im /dashboard/invoices
-Pfad aufgerufen wird, müssen Sie redirect
nicht aufrufen. Der Aufruf von revalidatePath
löst eine neue Server-Anfrage aus und rendert die Tabelle neu.
Weiterführende Lektüre
In diesem Kapitel haben Sie gelernt, wie Sie Server-Aktionen verwenden, um Daten zu mutieren. Sie haben auch gelernt, wie Sie die revalidatePath
-API verwenden, um den Next.js-Cache zu revalidieren, und redirect
, um den Nutzer zu einer neuen Seite weiterzuleiten.
Sie können auch mehr über Sicherheit mit Server-Aktionen lesen, um weiter zu lernen.