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:
- Forenklet Kode: Reduser boilerplate-kode ved å eliminere behovet for separate API-ruter.
- Forbedret Sikkerhet: Utførelse på serversiden minimerer sårbarheter på klientsiden.
- Økt Ytelse: Utfør datamutasjoner direkte på serveren for raskere responstider.
- Optimalisert SEO: Dra nytte av server-side rendering for bedre søkemotorindeksering.
- Typesikkerhet: Dra nytte av ende-til-ende typesikkerhet med TypeScript.
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:
- Direktivet
'use client'
indikerer at dette er en klientkomponent. - Funksjonen
createItem
er merket med direktivet'use server'
, som indikerer at det er en Server Action. - Funksjonen
handleSubmit
er en klient-side funksjon som kaller server-aksjonen. Den håndterer også UI-tilstand som å deaktivere knappen under innsending. <form>
-elementetsaction
-prop er satt tilhandleSubmit
-funksjonen.- Metoden
formData.get('name')
henter verdien av 'name'-inndatafeltet. await new Promise
simulerer en databaseoperasjon og legger til ventetid.
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:
- Funksjonen
createItem
sjekker nå omname
er gyldig (minst 3 tegn langt). - Hvis valideringen mislykkes, kastes en feil.
- Funksjonen
handleSubmit
er oppdatert for å fange opp eventuelle feil som kastes av Server Action og vise en feilmelding til brukeren.
Bruke Valideringsbiblioteker
For mer komplekse valideringsscenarier, vurder å bruke valideringsbiblioteker som:
- Zod: Et TypeScript-første skjema deklarasjons- og valideringsbibliotek.
- Yup: En JavaScript-skjemabygger for parsing, validering og transformering av verdier.
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:
CreateItemSchema
definerer valideringsreglene for feltetname
ved hjelp av Zod.- Metoden
safeParse
forsøker å validere inndataene. Hvis valideringen mislykkes, returnerer den et objekt med feilene. errors
-objektet inneholder detaljert informasjon om valideringsfeilene.
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:
- Funksjonen
getServerSession
henter brukerens sesjonsinformasjon. - Hvis brukeren ikke er autentisert (ingen sesjon), kastes en feil, noe som hindrer Server Action i å kjøre.
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:
- Før du kaller Server Action, oppdateres brukergrensesnittet umiddelbart med det nye elementnavnet ved hjelp av
setItemName
. - Hvis Server Action mislykkes, tilbakestilles brukergrensesnittet til det opprinnelige elementnavnet.
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å:
- Revalidate Path: Revalider bufferen for en spesifikk sti.
- Revalidate Tag: Revalider bufferen for data knyttet til en spesifikk tagg.
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:
- Funksjonen
revalidatePath('/items')
ugyldiggjør bufferen for/items
-stien, og sikrer at neste forespørsel til den stien henter de nyeste dataene.
Beste Praksis for Server Actions
For å maksimere fordelene med Server Actions, bør du vurdere følgende beste praksis:
- Hold Server Actions Små og Fokuserte: Server Actions bør utføre en enkelt, veldefinert oppgave. Unngå kompleks logikk i Server Actions for å opprettholde lesbarhet og testbarhet.
- Bruk Beskrivende Navn: Gi Server Actions beskrivende navn som tydelig indikerer formålet deres.
- Håndter Feil Elegant: Implementer robust feilhåndtering for å gi informativ tilbakemelding til brukeren og forhindre applikasjonskrasj.
- Valider Data Grundig: Utfør omfattende datavalidering for å sikre dataintegritet og forhindre sikkerhetssårbarheter.
- Sikre Server Actions: Implementer autentiserings- og autorisasjonsmekanismer for å beskytte sensitive data og funksjonalitet.
- Optimaliser Ytelse: Overvåk ytelsen til Server Actions og optimaliser dem etter behov for å sikre raske responstider.
- Utnytt Buffring Effektivt: Utnytt Next.js sine buffringsmekanismer for å forbedre ytelsen og redusere databasebelastningen.
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å:
- Overdrevent Komplekse Server Actions: Unngå å legge for mye logikk inn i en enkelt Server Action. Bryt ned komplekse oppgaver i mindre, mer håndterbare funksjoner.
- Forsømme Feilhåndtering: Inkluder alltid feilhåndtering for å fange opp uventede feil og gi nyttig tilbakemelding til brukeren.
- Ignorere Beste Praksis for Sikkerhet: Følg beste praksis for sikkerhet for å beskytte applikasjonen din mot vanlige trusler som XSS og CSRF.
- Glemme å Revalidere Data: Sørg for at du revaliderer bufret data etter at en Server Action endrer data for å holde brukergrensesnittet oppdatert.
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.