Norsk

En omfattende guide til Next.js 14 Server Actions, som dekker beste praksis for skjemahåndtering, datavalidering, sikkerhetshensyn og avanserte teknikker for å bygge moderne webapplikasjoner.

Next.js 14 Server Actions: Mestre Beste Praksis for Skjemahåndtering

Next.js 14 introduserer kraftige funksjoner for å bygge ytelsessterke og brukervennlige webapplikasjoner. Blant disse utmerker Server Actions seg som en transformativ måte å håndtere skjemainnsendinger og datamutasjoner direkte på serveren. Denne guiden gir en omfattende oversikt over Server Actions i Next.js 14, med fokus på beste praksis for skjemahåndtering, datavalidering, sikkerhet og avanserte teknikker. Vi vil utforske praktiske eksempler og gi handlingsrettet innsikt for å hjelpe deg med å bygge robuste og skalerbare webapplikasjoner.

Hva er Next.js Server Actions?

Server Actions er asynkrone funksjoner som kjører på serveren og kan påkalles direkte fra React-komponenter. De eliminerer behovet for tradisjonelle API-ruter for å håndtere skjemainnsendinger og datamutasjoner, noe som resulterer i forenklet kode, forbedret sikkerhet og økt ytelse. Server Actions er React Server Components (RSCs), som betyr at de utføres på serveren, noe som fører til raskere innlasting av den første siden og forbedret SEO.

Viktige Fordeler med Server Actions:

Sette Opp Ditt Next.js 14-Prosjekt

Før du dykker ned i Server Actions, må du sørge for at du har et Next.js 14-prosjekt satt opp. Hvis du starter fra bunnen av, oppretter du et nytt prosjekt ved hjelp av følgende kommando:

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

Sørg for at prosjektet ditt bruker app-katalogstrukturen for å dra full nytte av Server Components og Actions.

Grunnleggende Skjemahåndtering med Server Actions

La oss starte med et enkelt eksempel: et skjema som sender inn data for å opprette et nytt element i en database. Vi vil bruke et enkelt skjema med et inndatafelt og en send-knapp.

Eksempel: Opprette et Nytt Element

Først definerer du en Server Action-funksjon i React-komponenten din. Denne funksjonen vil håndtere logikken for skjemainnsending på serveren.

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

import { useState } from 'react';

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

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

  // Simulate database interaction
  console.log('Creating item:', name);

  await new Promise((resolve) => setTimeout(resolve, 1000)); // Simulate latency

  console.log('Item created successfully!');
}

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

  return (
    
); }

Forklaring:

Datavalidering

Datavalidering er avgjørende for å sikre dataintegritet og forhindre sikkerhetssårbarheter. Server Actions gir en utmerket mulighet til å utføre validering på serversiden. Denne tilnærmingen bidrar til å redusere risikoen forbundet med validering kun på klientsiden.

Eksempel: Validere Inndata

Endre createItem Server Action for å inkludere valideringslogikk.

// 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('Item name must be at least 3 characters long.');
  }

  // Simulate database interaction
  console.log('Creating item:', name);

  await new Promise((resolve) => setTimeout(resolve, 1000)); // Simulate latency

  console.log('Item created successfully!');
}

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 || 'An error occurred.');
    } finally {
      setIsSubmitting(false);
    }
  }

  return (
    
{errorMessage &&

{errorMessage}

}
); }

Forklaring:

Bruke Valideringsbiblioteker

For mer komplekse valideringsscenarier, vurder å bruke valideringsbiblioteker som:

Her er et eksempel ved hjelp av Zod:

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

export const CreateItemSchema = z.object({
  name: z.string().min(3, 'Item name must be at least 3 characters long.'),
});
// 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 };
  }

  // Simulate database interaction
  console.log('Creating item:', name);

  await new Promise((resolve) => setTimeout(resolve, 1000)); // Simulate latency

  console.log('Item created successfully!');
}

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 || 'An error occurred.');
    } finally {
      setIsSubmitting(false);
    }
  }

  return (
    
{errorMessage &&

{errorMessage}

}
); }

Forklaring:

Sikkerhetshensyn

Server Actions forbedrer sikkerheten ved å utføre kode på serveren, men det er fortsatt viktig å følge beste praksis for sikkerhet for å beskytte applikasjonen din mot vanlige trusler.

Forebygge Cross-Site Request Forgery (CSRF)

CSRF-angrep utnytter tilliten et nettsted har til en brukers nettleser. For å forhindre CSRF-angrep, implementer CSRF-beskyttelsesmekanismer.

Next.js håndterer automatisk CSRF-beskyttelse når du bruker Server Actions. Rammeverket genererer og validerer et CSRF-token for hver skjemainnsending, og sikrer at forespørselen kommer fra applikasjonen din.

Håndtere Brukerautentisering og Autorisering

Sørg for at bare autoriserte brukere kan utføre visse handlinger. Implementer autentiserings- og autorisasjonsmekanismer for å beskytte sensitive data og funksjonalitet.

Her er et eksempel ved hjelp av NextAuth.js for å beskytte 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('Unauthorized');
  }

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

  // Simulate database interaction
  console.log('Creating item:', name, 'by user:', session.user?.email);

  await new Promise((resolve) => setTimeout(resolve, 1000)); // Simulate latency

  console.log('Item created successfully!');
}

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 || 'An error occurred.');
    } finally {
      setIsSubmitting(false);
    }
  }

  return (
    
{errorMessage &&

{errorMessage}

}
); }

Forklaring:

Rense Inndata

Rens inndata for å forhindre Cross-Site Scripting (XSS)-angrep. XSS-angrep oppstår når ondsinnet kode injiseres i et nettsted, og potensielt kompromitterer brukerdata eller applikasjonsfunksjonalitet.

Bruk biblioteker som DOMPurify eller sanitize-html for å rense brukerdefinerte inndata før du behandler dem i Server Actions.

Avanserte Teknikker

Nå som vi har dekket det grunnleggende, la oss utforske noen avanserte teknikker for å bruke Server Actions effektivt.

Optimistiske Oppdateringer

Optimistiske oppdateringer gir en bedre brukeropplevelse ved å umiddelbart oppdatere brukergrensesnittet som om handlingen vil lykkes, selv før serveren bekrefter det. Hvis handlingen mislykkes på serveren, tilbakestilles brukergrensesnittet til sin forrige tilstand.

// 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;

  // Simulate database interaction
  console.log('Updating item:', id, 'with name:', name);

  await new Promise((resolve) => setTimeout(resolve, 1000)); // Simulate latency

  // Simulate failure (for demonstration purposes)
  const shouldFail = Math.random() < 0.5;
  if (shouldFail) {
    throw new Error('Failed to update item.');
  }

  console.log('Item updated successfully!');
  return { name }; // Return the updated name
}

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);

    // Optimistically update the UI
    const newName = formData.get('name') as string;
    setItemName(newName);

    try {
      const result = await updateItem(id, formData);
      //If success then update is already reflected in UI through setItemName

    } catch (error: any) {
      setErrorMessage(error.message || 'An error occurred.');
      // Revert the UI on error
      setItemName(initialName);
    } finally {
      setIsSubmitting(false);
    }
  }

  return (
    

Current Name: {itemName}

{errorMessage &&

{errorMessage}

}
); }

Forklaring:

Revalidere Data

Etter at en Server Action endrer data, kan det hende du må revalidere bufret data for å sikre at brukergrensesnittet gjenspeiler de siste endringene. Next.js tilbyr flere måter å revalidere data på:

Her er et eksempel på revalidering av en sti etter at du har opprettet et nytt element:

// 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;

  // Simulate database interaction
  console.log('Creating item:', name);

  await new Promise((resolve) => setTimeout(resolve, 1000)); // Simulate latency

  console.log('Item created successfully!');

  revalidatePath('/items'); // Revalidate the /items path
}

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 || 'An error occurred.');
    } finally {
      setIsSubmitting(false);
    }
  }

  return (
    
{errorMessage &&

{errorMessage}

}
); }

Forklaring:

Beste Praksis for Server Actions

For å maksimere fordelene med Server Actions, bør du vurdere følgende beste praksis:

Vanlige Fallgruver og Hvordan Unngå Dem

Selv om Server Actions tilbyr mange fordeler, er det noen vanlige fallgruver du bør være oppmerksom på:

Konklusjon

Next.js 14 Server Actions gir en kraftig og effektiv måte å håndtere skjemainnsendinger og datamutasjoner direkte på serveren. Ved å følge beste praksis som er skissert i denne guiden, kan du bygge robuste, sikre og ytelsessterke webapplikasjoner. Omfavn Server Actions for å forenkle koden din, forbedre sikkerheten og forbedre den generelle brukeropplevelsen. Når du integrerer disse prinsippene, bør du vurdere den globale innvirkningen av utviklingsvalgene dine. Sørg for at skjemaene og databehandlingsprosessene dine er tilgjengelige, sikre og brukervennlige for et mangfoldig internasjonalt publikum. Denne forpliktelsen til inkludering vil ikke bare forbedre applikasjonens brukervennlighet, men også utvide rekkevidden og effektiviteten i global skala.