Server Actions i praktiken
Hur Server Actions fungerar i Next.js - formulärhantering, validering, optimistiska uppdateringar och säkerhetsaspekter med verkliga kodexempel.
Vad är Server Actions?
Server Actions är asynkrona funktioner som körs på servern men anropas från klienten - eller direkt från Server Components. De förenklar formulärhantering och datamutationer drastiskt: ingen separat API-rutt behövs för de flesta operationer.
Du markerar en funktion som Server Action med "use server"-direktivet, antingen i en separat actions-fil eller direkt i en Server Component.
Din första Server Action
"use server";
export async function skickaFormular(formData) {
const namn = formData.get("namn");
const meddelande = formData.get("meddelande");
await sparaTillDatabas({ namn, meddelande });
}
Koppla den sedan direkt till ett formulärs action-prop. Next.js hanterar resten - inga fetch-anrop, inga API-rutter.
import { skickaFormular } from "./actions";
export default function KontaktPage() {
return (
<form action={skickaFormular}>
<input name="namn" required />
<textarea name="meddelande" required />
<button type="submit">Skicka</button>
</form>
);
}
Formuläret fungerar även utan JavaScript i webbläsaren - en progressiv förstärkning som Pages Router aldrig erbjöd lika enkelt.
Validering och felhantering
Validera alltid på servern. Klientsidevalidering är enbart en bekvämlighet för användaren, aldrig en säkerhetsgräns.
"use server";
export async function skickaFormular(prevState, formData) {
const namn = formData.get("namn");
if (!namn || namn.trim().length < 2) {
return { fel: "Namnet måste vara minst 2 tecken." };
}
await sparaTillDatabas({ namn: namn.trim() });
return { lyckades: true };
}
Använd useActionState på klientsidan för att ta emot svar från actionen och visa dem i formuläret:
"use client";
import { useActionState } from "react";
import { skickaFormular } from "./actions";
export default function KontaktFormular() {
const [state, action, isPending] = useActionState(skickaFormular, null);
return (
<form action={action}>
{state?.fel && <p>{state.fel}</p>}
{state?.lyckades && <p>Tack, vi hör av oss!</p>}
<input name="namn" required />
<button type="submit" disabled={isPending}>
{isPending ? "Skickar..." : "Skicka"}
</button>
</form>
);
}
Optimistiska uppdateringar
Med useOptimistic kan du uppdatera UI:t direkt - innan servern bekräftat - för en känsla av snabbhet. Pendande poster visas med reducerad opacitet tills actionen är klar.
"use client";
import { useOptimistic } from "react";
import { laddaUppKommentar } from "./actions";
export default function Kommentarsfält({ kommentarer }) {
const [visadeKommentarer, addOptimistic] = useOptimistic(
kommentarer,
(state, nyKommentar) => [...state, { ...nyKommentar, pending: true }],
);
async function skicka(formData) {
const text = formData.get("text");
addOptimistic({ text, id: Date.now() });
await laddaUppKommentar(formData);
}
return (
<div>
{visadeKommentarer.map((k) => (
<p key={k.id} style={{ opacity: k.pending ? 0.5 : 1 }}>
{k.text}
</p>
))}
<form action={skicka}>
<input name="text" required />
<button type="submit">Kommentera</button>
</form>
</div>
);
}
Säkerhetsaspekter
Server Actions exponeras som HTTP-endpoints och bör behandlas med samma säkerhetsmedvetenhet som API-rutter.
"use server";
import { getSession } from "@/lib/auth";
export async function raderaPost(postId) {
const session = await getSession();
if (!session) {
throw new Error("Inte autentiserad.");
}
if (session.roll !== "admin") {
throw new Error("Behörighet saknas.");
}
await taBortPost(postId);
}
Verifiera alltid session och behörighet inuti varje action. Förlita dig aldrig på att klienten inte anropar en action den inte borde anropa.
Sammanfattning
Server Actions ersätter många av de API-rutter du annars hade skapat. De är typsäkra, progressivt förstärkta och cacheinvalideringen hanteras automatiskt av Next.js. Börja med enkla formulär och introducera useOptimistic när du behöver responsivitet i gränssnittet.
