Next.js Server Actions nehmen dir die Pflicht ab, nur zur Verarbeitung eines Formulars eine separate API-Route zu schreiben: Du legst die Mutationslogik in eine Funktion, die auf dem Server läuft, und verbindest das Formular direkt damit. Das Ergebnis: Du kannst Daten anlegen und aktualisieren, ohne einen fetch-Aufruf, manuelle JSON-Serialisierung oder eine eigene app/api/...-Datei. In diesem Artikel richte ich Server Actions im App Router von Grund auf ein, ergänze Validierung und Fehlerbehandlung und gehe darauf ein, worauf du in der Produktion achten musst.
Was ist eine Server Action und wie funktioniert sie?
Eine Server Action ist eine asynchrone Funktion, deren Rumpf mit der Direktive 'use server' beginnt. Diese Funktion läuft ausschließlich auf dem Server; ihr Code landet nie im Client-Bundle. Beim Absenden des Formulars richtet Next.js den Netzwerkaufruf zwischen Browser und Server für dich ein. Du schreibst nur die Funktion, und Next.js macht daraus einen aufrufbaren Endpunkt.
Es gibt zwei Möglichkeiten der Platzierung: auf Modulebene in einer eigenen Datei mit 'use server' ganz oben, oder inline innerhalb einer Server Component definiert. Für wiederverwendbaren, testbaren Code bevorzuge ich den Ansatz mit separater Datei.
Schreibe deine erste Server Action
Legen wir die Action zuerst in eine eigene Datei. Das 'use server' oben in der Datei macht jede darin exportierte Funktion zu einer Server Action:
// app/actions.ts
'use server'
import { revalidatePath } from 'next/cache'
import { redirect } from 'next/navigation'
export async function createPost(formData: FormData) {
const title = formData.get('title')
const body = formData.get('body')
// hier in die Datenbank schreiben (Prisma, Drizzle, usw.)
await db.post.create({ data: { title, body } })
revalidatePath('/posts')
redirect('/posts')
}
Die Formularseite ist überraschend schlicht. Du übergibst dem action-Attribut die Funktion selbst, keine URL:
// app/posts/new/page.tsx
import { createPost } from '@/app/actions'
export default function NewPost() {
return (
<form action={createPost}>
<input name="title" />
<textarea name="body" />
<button type="submit">Speichern</button>
</form>
)
}
Beachte: Das Formular kann in einer Server Component leben und funktioniert sogar, wenn kein JavaScript geladen wurde. Das ist eine natürliche Folge von Progressive Enhancement.
Validiere die Eingabe — vertraue nie rohen Daten
Eine Server Action ist ein öffentlich erreichbarer Endpunkt; sie kann direkt aufgerufen werden, unabhängig davon, was du auf dem Client anzeigst. Validiere die eingehenden FormData daher immer auf dem Server. Schemabasierte Validierung mit Zod ist ein sauberer Ansatz:
'use server'
import { z } from 'zod'
const PostSchema = z.object({
title: z.string().min(3, 'Titel muss mindestens 3 Zeichen lang sein'),
body: z.string().min(10),
})
export async function createPost(prevState, formData: FormData) {
const parsed = PostSchema.safeParse({
title: formData.get('title'),
body: formData.get('body'),
})
if (!parsed.success) {
return { errors: parsed.error.flatten().fieldErrors }
}
await db.post.create({ data: parsed.data })
revalidatePath('/posts')
return { success: true }
}
safeParse gibt ein Ergebnisobjekt zurück, statt eine Ausnahme zu werfen, sodass du Validierungsfehler an den Nutzer zurückgeben kannst.
Fehler- und Statusbehandlung: useActionState
Um die von der obigen Action zurückgegebenen Fehler an das Formular zu binden, verwenden wir Reacts Hook useActionState (so heißt er in React 19 / Next.js 15; in älteren Versionen war es useFormState). Der Hook erfordert eine Client-Komponente:
'use client'
import { useActionState } from 'react'
import { createPost } from '@/app/actions'
export function PostForm() {
const [state, formAction, pending] = useActionState(createPost, {})
return (
<form action={formAction}>
<input name="title" />
{state?.errors?.title && <p>{state.errors.title}</p>}
<button disabled={pending}>
{pending ? 'Wird gespeichert...' : 'Speichern'}
</button>
</form>
)
}
Der dritte Rückgabewert, pending, ist true, während die Action läuft, und eignet sich perfekt, um den Button zu deaktivieren. Für feinkörnigere Ladeanzeigen kannst du den Hook useFormStatus in einer separaten Button-Komponente verwenden.
Cache aktualisieren: revalidatePath und revalidateTag
Nach einer Mutation muss der Bildschirm die aktuellen Daten anzeigen. Da Next.js Daten aggressiv cacht, machst du den betroffenen Pfad explizit ungültig:
revalidatePath('/posts')— leert den Cache einer bestimmten Route.revalidateTag('posts')— wenn du deinefetch-Aufrufe getaggt hast, aktualisiert dies alle an diesen Tag gebundenen Daten.redirect('/posts')— leitet den Nutzer nach der Mutation auf eine andere Seite; rufe es außerhalb jedestry/catch-Blocks auf, da es intern einen speziellen Fehler wirft.
Worauf du in der Produktion achten musst
- Autorisierung ist Pflicht: Eine Action ist ein Endpunkt. Prüfe Sitzung/Berechtigungen zu Beginn jeder Action; einen Button in der UI auszublenden ist keine Sicherheit.
- Seiteneffekte isolieren: Verlagere Datenbank, E-Mail und Zahlung in reine Funktionen, die von der Action aufgerufen werden; halte die Action als dünne Schicht.
- Rückgabewerte müssen serialisierbar sein: Der an den Client zurückgegebene State sollte aus einfachen Objekten/Arrays bestehen — keine Klasseninstanzen oder Funktionen.
- Du brauchst eventuell trotzdem eine API: Hast du eine mobile App oder eine Drittanbieter-Integration, ersetzt eine Server Action sie nicht; dann brauchst du eine echte API.
Häufige Fragen
Werden Server Actions nur mit Formularen verwendet?
Nein. Die Bindung an das action-Attribut ist der häufigste Weg, aber du kannst eine Server Action auch aus einem onClick-Handler eines Buttons oder innerhalb von startTransition aufrufen. Die Formularbindung wird bevorzugt, weil sie sogar funktioniert, wenn JavaScript deaktiviert ist.
Sollte ich API-Routen vollständig aufgeben?
Nein. Server Actions sind ideal für Mutationen, die aus deiner eigenen UI ausgelöst werden. Wenn du Webhooks, externe Clients oder eine öffentliche REST/JSON-Schnittstelle brauchst, sind klassische Route Handler (app/api/...) weiterhin das richtige Werkzeug.
Sind Server Actions sicher?
Die Infrastruktur ist sicher — Next.js schützt Action-Bezeichner und ordnet Übermittlungen über verschlüsselte Referenzen zu. Aber die Sicherheit deiner Geschäftslogik liegt bei dir: Validiere die Eingabe und prüfe die Autorisierung. Ohne das hinterlässt du einen offenen Endpunkt.
Werden Formulare in deinem Projekt durch Netzwerkcode kompliziert? Ich helfe dir, mit Next.js Server Actions eine saubere, validierte und sichere Datenschicht aufzubauen — nimm Kontakt mit mir auf.