Svenska

En omfattande guide till Next.js 14 Server Actions som täcker bästa praxis för formulärhantering, datavalidering, säkerhetsaspekter och avancerade tekniker för att bygga moderna webbapplikationer.

Next.js 14 Server Actions: Bemästra bästa praxis för formulärhantering

Next.js 14 introducerar kraftfulla funktioner för att bygga högpresterande och användarvänliga webbapplikationer. Bland dessa utmärker sig Server Actions som ett transformativt sätt att hantera formulärinskickningar och datamutationer direkt på servern. Den här guiden ger en omfattande översikt över Server Actions i Next.js 14, med fokus på bästa praxis för formulärhantering, datavalidering, säkerhet och avancerade tekniker. Vi kommer att utforska praktiska exempel och ge handlingsbara insikter för att hjälpa dig bygga robusta och skalbara webbapplikationer.

Vad är Next.js Server Actions?

Server Actions är asynkrona funktioner som körs på servern och kan anropas direkt från React-komponenter. De eliminerar behovet av traditionella API-rutter för att hantera formulärinskickningar och datamutationer, vilket resulterar i förenklad kod, förbättrad säkerhet och ökad prestanda. Server Actions är React Server Components (RSC), vilket innebär att de exekveras på servern, vilket leder till snabbare initiala sidladdningar och förbättrad SEO.

Viktiga fördelar med Server Actions:

Sätt upp ditt Next.js 14-projekt

Innan du dyker in i Server Actions, se till att du har ett Next.js 14-projekt uppsatt. Om du börjar från grunden, skapa ett nytt projekt med följande kommando:

npx create-next-app@latest my-next-app

Se till att ditt projekt använder app-katalogstrukturen för att dra full nytta av Server Components och Actions.

Grundläggande formulärhantering med Server Actions

Låt oss börja med ett enkelt exempel: ett formulär som skickar data för att skapa ett nytt objekt i en databas. Vi kommer att använda ett enkelt formulär med ett inmatningsfält och en skicka-knapp.

Exempel: Skapa ett nytt objekt

Definiera först en Server Action-funktion inom din React-komponent. Denna funktion kommer att hantera logiken för formulärinskickningen på servern.

// app/components/CreateItemForm.tsx
'use client';

import { useState } from 'react';

async function createItem(formData: FormData) {
  'use server'

  const name = formData.get('name') as string;

  // Simulera databasinteraktion
  console.log('Skapar objekt:', name);

  await new Promise((resolve) => setTimeout(resolve, 1000)); // Simulera latens

  console.log('Objekt skapat framgångsrikt!');
}

export default function CreateItemForm() {
  const [isSubmitting, setIsSubmitting] = useState(false);
  
  async function handleSubmit(formData: FormData) {
    setIsSubmitting(true);
    await createItem(formData);
    setIsSubmitting(false);
  }

  return (
    
); }

Förklaring:

Datavalidering

Datavalidering är avgörande för att säkerställa dataintegritet och förhindra säkerhetssårbarheter. Server Actions ger en utmärkt möjlighet att utföra validering på serversidan. Detta tillvägagångssätt hjälper till att minska risker som är förknippade med enbart klientsidig validering.

Exempel: Validering av indata

Modifiera createItem Server Action för att inkludera valideringslogik.

// app/components/CreateItemForm.tsx
'use client';

import { useState } from 'react';

async function createItem(formData: FormData) {
  'use server'

  const name = formData.get('name') as string;

  if (!name || name.length < 3) {
    throw new Error('Objektnamnet måste vara minst 3 tecken långt.');
  }

  // Simulera databasinteraktion
  console.log('Skapar objekt:', name);

  await new Promise((resolve) => setTimeout(resolve, 1000)); // Simulera latens

  console.log('Objekt skapat framgångsrikt!');
}

export default function CreateItemForm() {
  const [isSubmitting, setIsSubmitting] = useState(false);
  const [errorMessage, setErrorMessage] = useState(null);
  
  async function handleSubmit(formData: FormData) {
    setIsSubmitting(true);
    setErrorMessage(null);
    try {
      await createItem(formData);
    } catch (error: any) {
      setErrorMessage(error.message || 'Ett fel uppstod.');
    } finally {
      setIsSubmitting(false);
    }
  }

  return (
    
{errorMessage &&

{errorMessage}

}
); }

Förklaring:

Använda valideringsbibliotek

För mer komplexa valideringsscenarier, överväg att använda valideringsbibliotek som:

Här är ett exempel med Zod:

// app/utils/validation.ts
import { z } from 'zod';

export const CreateItemSchema = z.object({
  name: z.string().min(3, 'Objektnamnet måste vara minst 3 tecken långt.'),
});
// app/components/CreateItemForm.tsx
'use client';

import { useState } from 'react';
import { CreateItemSchema } from '../utils/validation';

async function createItem(formData: FormData) {
  'use server'

  const name = formData.get('name') as string;

  const validatedFields = CreateItemSchema.safeParse({ name });

  if (!validatedFields.success) {
    return { errors: validatedFields.error.flatten().fieldErrors };
  }

  // Simulera databasinteraktion
  console.log('Skapar objekt:', name);

  await new Promise((resolve) => setTimeout(resolve, 1000)); // Simulera latens

  console.log('Objekt skapat framgångsrikt!');
}

export default function CreateItemForm() {
  const [isSubmitting, setIsSubmitting] = useState(false);
  const [errorMessage, setErrorMessage] = useState(null);
  
  async function handleSubmit(formData: FormData) {
    setIsSubmitting(true);
    setErrorMessage(null);
    try {
      await createItem(formData);
    } catch (error: any) {
      setErrorMessage(error.message || 'Ett fel uppstod.');
    } finally {
      setIsSubmitting(false);
    }
  }

  return (
    
{errorMessage &&

{errorMessage}

}
); }

Förklaring:

Säkerhetsaspekter

Server Actions förbättrar säkerheten genom att exekvera kod på servern, men det är fortfarande avgörande att följa bästa praxis för säkerhet för att skydda din applikation från vanliga hot.

Förhindra Cross-Site Request Forgery (CSRF)

CSRF-attacker utnyttjar det förtroende som en webbplats har för en användares webbläsare. För att förhindra CSRF-attacker, implementera mekanismer för CSRF-skydd.

Next.js hanterar automatiskt CSRF-skydd när du använder Server Actions. Ramverket genererar och validerar en CSRF-token för varje formulärinskickning, vilket säkerställer att begäran kommer från din applikation.

Hantera användarautentisering och auktorisering

Säkerställ att endast auktoriserade användare kan utföra vissa åtgärder. Implementera autentiserings- och auktoriseringsmekanismer för att skydda känsliga data och funktioner.

Här är ett exempel med NextAuth.js för att skydda en Server Action:

// app/components/CreateItemForm.tsx
'use client';

import { useState } from 'react';
import { getServerSession } from 'next-auth';
import { authOptions } from '../../app/api/auth/[...nextauth]/route';

async function createItem(formData: FormData) {
  'use server'

  const session = await getServerSession(authOptions);

  if (!session) {
    throw new Error('Obehörig');
  }

  const name = formData.get('name') as string;

  // Simulera databasinteraktion
  console.log('Skapar objekt:', name, 'av användare:', session.user?.email);

  await new Promise((resolve) => setTimeout(resolve, 1000)); // Simulera latens

  console.log('Objekt skapat framgångsrikt!');
}

export default function CreateItemForm() {
  const [isSubmitting, setIsSubmitting] = useState(false);
  const [errorMessage, setErrorMessage] = useState(null);
  
  async function handleSubmit(formData: FormData) {
    setIsSubmitting(true);
    setErrorMessage(null);
    try {
      await createItem(formData);
    } catch (error: any) {
      setErrorMessage(error.message || 'Ett fel uppstod.');
    } finally {
      setIsSubmitting(false);
    }
  }

  return (
    
{errorMessage &&

{errorMessage}

}
); }

Förklaring:

Sanera indata

Sanera indata för att förhindra Cross-Site Scripting (XSS)-attacker. XSS-attacker inträffar när skadlig kod injiceras på en webbplats, vilket potentiellt kan kompromettera användardata eller applikationsfunktionalitet.

Använd bibliotek som DOMPurify eller sanitize-html för att sanera användartillhandahållen indata innan den bearbetas i dina Server Actions.

Avancerade tekniker

Nu när vi har gått igenom grunderna, låt oss utforska några avancerade tekniker för att använda Server Actions effektivt.

Optimistiska uppdateringar

Optimistiska uppdateringar ger en bättre användarupplevelse genom att omedelbart uppdatera gränssnittet som om åtgärden kommer att lyckas, redan innan servern bekräftar det. Om åtgärden misslyckas på servern återställs gränssnittet till sitt tidigare tillstånd.

// app/components/UpdateItemForm.tsx
'use client';

import { useState } from 'react';

async function updateItem(id: string, formData: FormData) {
  'use server'

  const name = formData.get('name') as string;

  // Simulera databasinteraktion
  console.log('Uppdaterar objekt:', id, 'med namn:', name);

  await new Promise((resolve) => setTimeout(resolve, 1000)); // Simulera latens

  // Simulera misslyckande (för demonstrationsändamål)
  const shouldFail = Math.random() < 0.5;
  if (shouldFail) {
    throw new Error('Misslyckades med att uppdatera objekt.');
  }

  console.log('Objekt uppdaterat framgångsrikt!');
  return { name }; // Returnera det uppdaterade namnet
}

export default function UpdateItemForm({ id, initialName }: { id: string; initialName: string }) {
  const [isSubmitting, setIsSubmitting] = useState(false);
  const [errorMessage, setErrorMessage] = useState(null);
  const [itemName, setItemName] = useState(initialName);

  async function handleSubmit(formData: FormData) {
    setIsSubmitting(true);
    setErrorMessage(null);

    // Uppdatera gränssnittet optimistiskt
    const newName = formData.get('name') as string;
    setItemName(newName);

    try {
      const result = await updateItem(id, formData);
      // Om lyckat återspeglas uppdateringen redan i gränssnittet via setItemName

    } catch (error: any) {
      setErrorMessage(error.message || 'Ett fel uppstod.');
      // Återställ gränssnittet vid fel
      setItemName(initialName);
    } finally {
      setIsSubmitting(false);
    }
  }

  return (
    

Nuvarande namn: {itemName}

{errorMessage &&

{errorMessage}

}
); }

Förklaring:

Revalidera data

Efter att en Server Action har modifierat data kan du behöva revalidera cachad data för att säkerställa att gränssnittet återspeglar de senaste ändringarna. Next.js erbjuder flera sätt att revalidera data:

Här är ett exempel på hur man revaliderar en sökväg efter att ha skapat ett nytt objekt:

// app/components/CreateItemForm.tsx
'use client';

import { useState } from 'react';
import { revalidatePath } from 'next/cache';

async function createItem(formData: FormData) {
  'use server'

  const name = formData.get('name') as string;

  // Simulera databasinteraktion
  console.log('Skapar objekt:', name);

  await new Promise((resolve) => setTimeout(resolve, 1000)); // Simulera latens

  console.log('Objekt skapat framgångsrikt!');

  revalidatePath('/items'); // Revalidera sökvägen /items
}

export default function CreateItemForm() {
  const [isSubmitting, setIsSubmitting] = useState(false);
  const [errorMessage, setErrorMessage] = useState(null);
  
  async function handleSubmit(formData: FormData) {
    setIsSubmitting(true);
    setErrorMessage(null);
    try {
      await createItem(formData);
    } catch (error: any) {
      setErrorMessage(error.message || 'Ett fel uppstod.');
    } finally {
      setIsSubmitting(false);
    }
  }

  return (
    
{errorMessage &&

{errorMessage}

}
); }

Förklaring:

Bästa praxis för Server Actions

För att maximera fördelarna med Server Actions, överväg följande bästa praxis:

Vanliga fallgropar och hur man undviker dem

Även om Server Actions erbjuder många fördelar finns det några vanliga fallgropar att vara medveten om:

Slutsats

Next.js 14 Server Actions erbjuder ett kraftfullt och effektivt sätt att hantera formulärinskickningar och datamutationer direkt på servern. Genom att följa de bästa praxis som beskrivs i denna guide kan du bygga robusta, säkra och högpresterande webbapplikationer. Omfamna Server Actions för att förenkla din kod, förbättra säkerheten och förbättra den övergripande användarupplevelsen. När du integrerar dessa principer, överväg den globala inverkan av dina utvecklingsval. Se till att dina formulär och datahanteringsprocesser är tillgängliga, säkra och användarvänliga för olika internationella målgrupper. Detta engagemang för inkludering kommer inte bara att förbättra din applikations användbarhet utan också bredda dess räckvidd och effektivitet på en global skala.