← Guider för utvecklare
Avancerat12 min läsning

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.

Kevin Sommerstein
Kevin SommersteinGrundare, Developly

Medgrundare av Developly Sweden och webbutvecklare med 8 års erfarenhet inom JavaScript, React och Next.js.

LinkedIn →