Next.js
React
Server Actions
Fullstack
Web Development

Next.js 15 und Serveraktionen: Der endgültige Leitfaden für moderne Mutationen

Next.js 15 und Serveraktionen: Der endgültige Leitfaden für moderne Mutationen

Webentwicklung ist im ständigen Wandel. Es gab eine Zeit, in der alles auf dem Server gerendert wurde (PHP, Ruby on Rails). Dann verschieben wir alles auf den Client (SPAs mit React, Vue). Mit der Einführung von React Server Components (RSC) und Next.js App Router finden wir nun einen leistungsstarken Mittelweg: das Beste aus beiden Welten.

Und das Kronjuwel dieser neuen Ära im React-Ökosystem sind Serveraktionen.

Mit der Veröffentlichung von Next.js 15 sind Serveraktionen nicht mehr experimentell und wurden zum empfohlenen Standard für den Umgang mit Datenmutationen. In diesem umfassenden Leitfaden werden wir jedes Detail dieser Funktion untersuchen, von den Grundlagen bis hin zu erweiterten Sicherheits- und UX-Standards. Wenn Sie immer noch pages/api/submit.ts schreiben, um Formulare zu verarbeiten, bereiten Sie sich darauf vor, einen Großteil des Boilerplate-Codes zurückzuziehen.

Was sind eigentlich Serveraktionen?

Vereinfacht ausgedrückt sind Serveraktionen asynchrone Funktionen, die auf dem Server ausgeführt werden, aber direkt von Client- oder Serverkomponenten aufgerufen werden können.

Bisher war der Ablauf zum Absenden eines Formulars wie folgt:

  1. Erstellen Sie eine Formularkomponente auf dem Client ("use client").
  2. Erstellen Sie einen useState-Zustand für die Eingänge.
  3. Erstellen Sie eine Funktion onSubmit, die ein fetch('/api/submit', ...) ausführt.
  4. Erstellen Sie eine API-Route-Datei (pages/api/submit.ts), um die Anfrage zu empfangen, zu validieren und in der Datenbank zu speichern.
  5. Behandeln Sie Fehler, Ladezustände und Datenüberprüfung manuell.

Bei Serveraktionen ist der Ablauf:

  1. Erstellen Sie eine Funktion async, die in der Bank speichert.
  2. Übergeben Sie diese Funktion an prop action von <form>.

Ende. Next.js kümmert sich um Kommunikation, Serialisierung und Ausführung. Es handelt sich um einen Remote Procedure Call (RPC), der genau für das Web entwickelt wurde.

Erstkonfiguration in Next.js 15

In Next.js 15 sind Serveraktionen bereits standardmäßig aktiviert. Sie müssen den next.config.js nicht wechseln.

Die Konvention ist einfach:

  • Um Aktionen zu definieren, die in Client-Komponenten importiert werden können, erstellen Sie eine Datei mit der Anweisung "use server" oben.
  • Für Inline-Aktionen in Serverkomponenten fügen Sie "use server" innerhalb der Funktion hinzu.

Einfaches Beispiel: Die „Hallo Welt“ der Aktionen

Erstellen wir eine Aktion zum Speichern eines Blogbeitrags. Erstellen Sie eine src/app/actions.ts-Datei:

// src/app/actions.ts 'use server' import { db } from '@/lib/db' import { revalidatePath } from 'next/cache' export async function createPost(formData: FormData) { const title = formData.get('title') as string const content = formData.get('content') as string // Validação básica (em produção use Zod!) if (!title || !content) { throw new Error('Campos obrigatórios faltando') } // Interação direta com o banco (sem API Routes!) await db.post.create({ data: { title, content } }) // A mágica: atualiza a UI instantaneamente revalidatePath('/blog') }

Und in Ihrer Komponente:

// src/components/create-post-form.tsx import { createPost } from '@/app/actions' export function CreatePostForm() { return ( <form action={createPost} className="p-4 border rounded"> <input name="title" placeholder="Título" className="border p-2 mb-2 w-full" /> <textarea name="content" placeholder="Conteúdo" className="border p-2 w-full" /> <button type="submit" className="bg-blue-500 text-white p-2 rounded"> Salvar Post </button> </form> ) }

Beachten Sie, dass auf dem Client kein JavaScript für die Übermittlung vorhanden ist. Dieses Formular funktioniert auch dann, wenn der Benutzer JS im Browser deaktiviert (Progressive Enhancement), obwohl wir in modernen Anwendungen selten darauf angewiesen sind.

Ladezustandsverwaltung (useFormStatus)

Bad UX klickt auf „Speichern“ und erhält kein Feedback. Da wir die native HTML-Requisite action verwenden, haben wir kein useState(loading)-Handbuch. React stellt uns dafür den useFormStatus-Haken zur Verfügung.

Hinweis: useFormStatus müssen in einer Komponente verwendet werden, die innerhalb <form> gerendert wird.

// src/components/submit-button.tsx 'use client' import { useFormStatus } from 'react-dom' export function SubmitButton() { const { pending } = useFormStatus() return ( <button type="submit" disabled={pending} className="bg-blue-500 disabled:bg-gray-400 text-white p-2 rounded flex items-center gap-2" > {pending ? 'Salvando...' : 'Salvar Post'} {pending && <Spinner />} </button> ) }

Verwenden Sie jetzt einfach <SubmitButton /> innerhalb des Formulars im vorherigen Beispiel.

Fehlerbehandlung und Feedback (useActionState)

Was passiert, wenn Datenbank ausfällt? Oder Validierung? Einfach einen Fehler (throw new Error) auszulösen, ist nicht die beste UX. Wir möchten Fehlermeldungen an das Formular zurückgeben.

Dazu verwenden wir den Hook useActionState (zuvor useFormState im experimentellen React). Dadurch kann die Serveraktion einen Wert zurückgeben, der auf dem Client aktualisiert wird.

Refactoring unserer Aktion:

// src/app/actions.ts 'use server' export type FormState = { message: string; errors?: { title?: string[]; content?: string[]; }; } export async function createPostSafely(prevState: FormState, formData: FormData): Promise<FormState> { // Simulação de validação com Zod const validatedFields = schema.safeParse({ title: formData.get('title'), content: formData.get('content'), }); if (!validatedFields.success) { return { message: 'Erro de validação', errors: validatedFields.error.flatten().fieldErrors } } try { await db.post.create({ data: validatedFields.data }) } catch (e) { return { message: 'Erro ao salvar no banco de dados' } } revalidatePath('/blog') return { message: 'Post criado com sucesso!' } }

Und in der Client-Komponente:

'use client' import { useActionState } from 'react' import { createPostSafely } from '@/app/actions' const initialState = { message: '', errors: {} } export function AdvancedForm() { const [state, formAction] = useActionState(createPostSafely, initialState) return ( <form action={formAction}> {state.message && <p className="text-red-500">{state.message}</p>} <input name="title" /> {state.errors?.title && <p className="text-red-500 text-sm">{state.errors.title[0]}</p>} {/* ... restante do form ... */} </form> ) }

Cache-Revalidierung: Die Kraft von revalidatePath

Im alten Modell musste man nach einer Mutation die Daten manuell erneut abrufen oder Bibliotheken wie React Query oder SWR verwenden, um Caches ungültig zu machen.

In Next.js ist dies integriert. Die Funktion revalidatePath(path) löscht den Cache für diese bestimmte Route. Beim nächsten Besuch (oder sofort, wenn es sich um eine SPA-Navigation handelt) ruft Next.js die neuen Daten vom Server ab. Es gibt auch revalidateTag(tag), das Ihnen eine detaillierte Kontrolle gibt, wenn Sie fetch mit (fetch(url, { next: { tags: ['posts'] } }))-Tags verwenden.

Optimistische Updates (useOptimistic)

Bei Anwendungen, die „sofort“ angezeigt werden, möchten wir nicht darauf warten, dass der Server antwortet, um die Benutzeroberfläche zu aktualisieren. Wenn ich der Liste ein Element hinzufüge, möchte ich es jetzt dort sehen.

Der Haken useOptimistic ermöglicht dies auf elegante Weise.

'use client' import { useOptimistic } from 'react' export function PostList({ posts }: { posts: Post[] }) { const [optimisticPosts, addOptimisticPost] = useOptimistic( posts, (state, newPost: Post) => [newPost, ...state] ); async function action(formData: FormData) { const title = formData.get('title') as string; // Atualiza a UI imediatamente addOptimisticPost({ id: Math.random(), title, content: '...' }); // Chama a Server Action real await createPost(formData); } return ( <div> <form action={action}>...</form> <ul> {optimisticPosts.map(post => ( <li key={post.id}>{post.title}</li> ))} </ul> </div> ) }

Wenn die Serveraktion fehlschlägt, setzt React den optimistischen Zustand automatisch auf den vorherigen realen Zustand zurück. Es ist robust und magisch.

Sicherheit: Der Elefant im Raum

Viele Entwickler schauen sich "use server" an und denken: „Warten Sie, mache ich meine Datenbank dem Client zugänglich?“

Nein. Der Serveraktionscode wird niemals an den Browser gesendet. Nur die „Signatur“ der Funktion (eine interne Next.js-Referenz-URL) wird offengelegt. Es ist jedoch wichtig, Serveraktionen als öffentliche Einstiegspunkte zu Ihrer API zu behandeln.

Obligatorische Sicherheitscheckliste:

  1. Authentifizierung: Überprüfen Sie immer, wer die Aktion aufruft. Gehen Sie nicht davon aus, dass der Benutzer angemeldet ist, nur weil die Schaltfläche „Speichern“ sichtbar war.

    import { auth } from '@/auth' // Auth.js ou Clerk export async function deletePost(id: string) { const session = await auth() if (!session?.user) throw new Error('Não autorizado') // ... }
  2. Autorisierung: Der Benutzer ist angemeldet, aber kann er diesen Beitrag *löschen?

    const post = await db.post.findUnique({ where: { id } }) if (post.authorId !== session.user.id) throw new Error('Proibido')
  3. Eingabevalidierung: Vertrauen Sie niemals FormData oder übergebenen Argumenten. Verwenden Sie Zod, um sicherzustellen, dass die Daten das richtige Format haben.

Serveraktionen vs. API-Routen

Sind API Routes schließlich tot?

Nicht ganz, aber seine Verwendung ist drastisch zurückgegangen.

Verwenden Sie Serveraktionen, wenn:

  • Sie haben es mit Formmutationen zu tun.
  • Sie möchten eine Serverfunktion aus einem Ereignis auf dem Client aufrufen (onClick).
  • Sie möchten TypeScript End-to-End-Typisierung ohne Generierung von SDKs.

Verwenden Sie API-Routen (Routenhandler), wenn:

  • Sie müssen eine öffentliche [REST API47 für Dritte (Webhooks, mobile Apps) verfügbar machen.
  • Sie benötigen bestimmte Funktionen des HTTP-Protokolls, die von React-Abstraktionen ausgeblendet werden (z. B. Streaming benutzerdefinierter Binärdateien, komplexe Header).

Fazit

Next.js 15 festigt Serveraktionen als eine der produktivsten Änderungen in der jüngsten Webentwicklung. Sie eliminieren die mittlere Schicht des „Klebercodes“ (API-Routen, Abruffunktionen, Statusmanager) und ermöglichen es Ihnen, sich auf das Wesentliche zu konzentrieren: Geschäftslogik und Benutzeroberfläche.

Durch die Kombination von Serveraktionen mit useActionState, useOptimistic und der Validierung mit Zod erstellen Sie robuste, sichere Anwendungen mit einem erstklassigen Benutzererlebnis und schreiben dabei nur einen Bruchteil des Codes, den Sie vor drei Jahren geschrieben hätten.

Die Zukunft liegt serverseitig, aber das Erlebnis ist clientseitig. Und Serveraktionen sind die Brücke zwischen ihnen.


Haben Sie Ihre Formulare bereits auf Serveraktionen migriert? Was war die größte Herausforderung? Hinterlassen Sie Ihren Kommentar!

Lesen Sie auch