En komplet guide til Next.js 14 Server Actions. Lær bedste praksisser for formularhåndtering, datavalidering, sikkerhed og avancerede teknikker.
Next.js 14 Server Actions: Bedste praksisser for formularhåndtering
Next.js 14 introducerer kraftfulde funktioner til at bygge performante og brugervenlige webapplikationer. Blandt disse skiller Server Actions sig ud som en transformerende måde at håndtere formularindsendelser og datamutationer direkte på serveren. Denne guide giver et omfattende overblik over Server Actions i Next.js 14, med fokus på bedste praksisser for formularhåndtering, datavalidering, sikkerhed og avancerede teknikker. Vi vil udforske praktiske eksempler og give handlingsorienterede indsigter for at hjælpe dig med at bygge robuste og skalerbare webapplikationer.
Hvad er Next.js Server Actions?
Server Actions er asynkrone funktioner, der kører på serveren og kan kaldes direkte fra React-komponenter. De eliminerer behovet for traditionelle API-ruter til håndtering af formularindsendelser og datamutationer, hvilket resulterer i forenklet kode, forbedret sikkerhed og øget ydeevne. Server Actions er React Server Components (RSC'er), hvilket betyder, at de udføres på serveren, hvilket fører til hurtigere indlæsning af sider og forbedret SEO.
Væsentlige fordele ved Server Actions:
- Forenklet kode: Reducer standardkode ved at eliminere behovet for separate API-ruter.
- Forbedret sikkerhed: Server-side eksekvering minimerer sårbarheder på klientsiden.
- Forbedret ydeevne: Udfør datamutationer direkte på serveren for hurtigere svartider.
- Optimeret SEO: Udnyt server-side rendering for bedre indeksering i søgemaskiner.
- Typesikkerhed: Drag fordel af end-to-end typesikkerhed med TypeScript.
Opsætning af dit Next.js 14-projekt
Før du dykker ned i Server Actions, skal du sikre dig, at du har et Next.js 14-projekt opsat. Hvis du starter fra bunden, kan du oprette et nyt projekt med følgende kommando:
npx create-next-app@latest my-next-app
Sørg for, at dit projekt bruger app
-mappestrukturen for at drage fuld fordel af Server Components og Actions.
Grundlæggende formularhåndtering med Server Actions
Lad os starte med et simpelt eksempel: en formular, der sender data for at oprette et nyt element i en database. Vi vil bruge en simpel formular med et inputfelt og en submit-knap.
Eksempel: Oprettelse af et nyt element
Først skal du definere en Server Action-funktion i din React-komponent. Denne funktion vil håndtere logikken for formularindsendelse 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;
// Simuler interaktion med databasen
console.log('Opretter element:', name);
await new Promise((resolve) => setTimeout(resolve, 1000)); // Simuler latenstid
console.log('Element oprettet succesfuldt!');
}
export default function CreateItemForm() {
const [isSubmitting, setIsSubmitting] = useState(false);
async function handleSubmit(formData: FormData) {
setIsSubmitting(true);
await createItem(formData);
setIsSubmitting(false);
}
return (
);
}
Forklaring:
'use client'
-direktivet angiver, at dette er en klientkomponent.createItem
-funktionen er markeret med'use server'
-direktivet, hvilket angiver, at det er en Server Action.handleSubmit
-funktionen er en klient-side funktion, der kalder serverhandlingen. Den håndterer også UI-tilstande som f.eks. at deaktivere knappen under indsendelse.<form>
-elementetsaction
-prop er sat tilhandleSubmit
-funktionen.formData.get('name')
-metoden henter værdien fra 'name'-inputfeltet.await new Promise
simulerer en databaseoperation og tilføjer latenstid.
Datavalidering
Datavalidering er afgørende for at sikre dataintegritet og forhindre sikkerhedssårbarheder. Server Actions giver en fremragende mulighed for at udføre server-side validering. Denne tilgang hjælper med at mindske risici forbundet med kun at have klient-side validering.
Eksempel: Validering af inputdata
Modificer createItem
Server Action til at inkludere 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('Navnet på elementet skal være mindst 3 tegn langt.');
}
// Simuler interaktion med databasen
console.log('Opretter element:', name);
await new Promise((resolve) => setTimeout(resolve, 1000)); // Simuler latenstid
console.log('Element oprettet succesfuldt!');
}
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 || 'Der opstod en fejl.');
} finally {
setIsSubmitting(false);
}
}
return (
{errorMessage && {errorMessage}
}
);
}
Forklaring:
createItem
-funktionen tjekker nu, omname
er gyldigt (mindst 3 tegn langt).- Hvis valideringen mislykkes, kastes en fejl.
handleSubmit
-funktionen er opdateret til at fange eventuelle fejl, der kastes af Server Action, og vise en fejlmeddelelse til brugeren.
Brug af valideringsbiblioteker
For mere komplekse valideringsscenarier kan du overveje at bruge valideringsbiblioteker som:
- Zod: Et TypeScript-first skema-deklarations- og valideringsbibliotek.
- Yup: En JavaScript skemabygger til at parse, validere og transformere værdier.
Her er et eksempel, der bruger Zod:
// app/utils/validation.ts
import { z } from 'zod';
export const CreateItemSchema = z.object({
name: z.string().min(3, 'Navnet på elementet skal være mindst 3 tegn langt.'),
});
// 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 };
}
// Simuler interaktion med databasen
console.log('Opretter element:', name);
await new Promise((resolve) => setTimeout(resolve, 1000)); // Simuler latenstid
console.log('Element oprettet succesfuldt!');
}
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 || 'Der opstod en fejl.');
} finally {
setIsSubmitting(false);
}
}
return (
{errorMessage && {errorMessage}
}
);
}
Forklaring:
CreateItemSchema
definerer valideringsreglerne forname
-feltet ved hjælp af Zod.safeParse
-metoden forsøger at validere inputdataene. Hvis valideringen mislykkes, returnerer den et objekt med fejlene.errors
-objektet indeholder detaljerede oplysninger om valideringsfejlene.
Sikkerhedsovervejelser
Server Actions forbedrer sikkerheden ved at eksekvere kode på serveren, men det er stadig afgørende at følge bedste praksisser for sikkerhed for at beskytte din applikation mod almindelige trusler.
Forebyggelse af Cross-Site Request Forgery (CSRF)
CSRF-angreb udnytter den tillid, et website har til en brugers browser. For at forhindre CSRF-angreb skal du implementere mekanismer til CSRF-beskyttelse.
Next.js håndterer automatisk CSRF-beskyttelse, når du bruger Server Actions. Frameworket genererer og validerer et CSRF-token for hver formularindsendelse, hvilket sikrer, at anmodningen stammer fra din applikation.
Håndtering af brugerautentificering og -autorisation
Sørg for, at kun autoriserede brugere kan udføre bestemte handlinger. Implementer autentificerings- og autorisationsmekanismer for at beskytte følsomme data og funktionalitet.
Her er et eksempel, der bruger NextAuth.js til at 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('Uautoriseret');
}
const name = formData.get('name') as string;
// Simuler interaktion med databasen
console.log('Opretter element:', name, 'af bruger:', session.user?.email);
await new Promise((resolve) => setTimeout(resolve, 1000)); // Simuler latenstid
console.log('Element oprettet succesfuldt!');
}
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 || 'Der opstod en fejl.');
} finally {
setIsSubmitting(false);
}
}
return (
{errorMessage && {errorMessage}
}
);
}
Forklaring:
getServerSession
-funktionen henter brugerens sessionsoplysninger.- Hvis brugeren ikke er autentificeret (ingen session), kastes en fejl, hvilket forhindrer Server Action i at blive udført.
Sanering af inputdata
Sanér inputdata for at forhindre Cross-Site Scripting (XSS)-angreb. XSS-angreb opstår, når ondsindet kode injiceres i et website, hvilket potentielt kan kompromittere brugerdata eller applikationsfunktionalitet.
Brug biblioteker som DOMPurify
eller sanitize-html
til at sanere brugerleveret input, før det behandles i dine Server Actions.
Avancerede teknikker
Nu hvor vi har dækket det grundlæggende, lad os udforske nogle avancerede teknikker til at bruge Server Actions effektivt.
Optimistiske opdateringer
Optimistiske opdateringer giver en bedre brugeroplevelse ved øjeblikkeligt at opdatere UI'en, som om handlingen vil lykkes, selv før serveren bekræfter det. Hvis handlingen mislykkes på serveren, gendannes UI'en til sin tidligere 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;
// Simuler interaktion med databasen
console.log('Opdaterer element:', id, 'med navn:', name);
await new Promise((resolve) => setTimeout(resolve, 1000)); // Simuler latenstid
// Simuler fejl (til demonstrationsformål)
const shouldFail = Math.random() < 0.5;
if (shouldFail) {
throw new Error('Kunne ikke opdatere element.');
}
console.log('Element opdateret succesfuldt!');
return { name }; // Returner det opdaterede navn
}
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);
// Opdater UI'en optimistisk
const newName = formData.get('name') as string;
setItemName(newName);
try {
const result = await updateItem(id, formData);
// Ved succes er opdateringen allerede afspejlet i UI'en via setItemName
} catch (error: any) {
setErrorMessage(error.message || 'Der opstod en fejl.');
// Gendan UI'en ved fejl
setItemName(initialName);
} finally {
setIsSubmitting(false);
}
}
return (
Nuværende navn: {itemName}
{errorMessage && {errorMessage}
}
);
}
Forklaring:
- Før Server Action kaldes, opdateres UI'en øjeblikkeligt med det nye elementnavn ved hjælp af
setItemName
. - Hvis Server Action mislykkes, gendannes UI'en til det oprindelige elementnavn.
Revalidering af data
Efter en Server Action har ændret data, kan det være nødvendigt at revalidere cachede data for at sikre, at UI'en afspejler de seneste ændringer. Next.js tilbyder flere måder at revalidere data på:
- Revalidate Path: Revalider cachen for en specifik sti.
- Revalidate Tag: Revalider cachen for data forbundet med et specifikt tag.
Her er et eksempel på revalidering af en sti efter oprettelse af et nyt 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;
// Simuler interaktion med databasen
console.log('Opretter element:', name);
await new Promise((resolve) => setTimeout(resolve, 1000)); // Simuler latenstid
console.log('Element oprettet succesfuldt!');
revalidatePath('/items'); // Revalider /items-stien
}
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 || 'Der opstod en fejl.');
} finally {
setIsSubmitting(false);
}
}
return (
{errorMessage && {errorMessage}
}
);
}
Forklaring:
revalidatePath('/items')
-funktionen invaliderer cachen for/items
-stien, hvilket sikrer, at den næste anmodning til den sti henter de seneste data.
Bedste praksisser for Server Actions
For at maksimere fordelene ved Server Actions, bør du overveje følgende bedste praksisser:
- Hold Server Actions små og fokuserede: Server Actions bør udføre en enkelt, veldefineret opgave. Undgå kompleks logik inden i Server Actions for at bevare læsbarhed og testbarhed.
- Brug beskrivende navne: Giv dine Server Actions beskrivende navne, der tydeligt angiver deres formål.
- Håndter fejl elegant: Implementer robust fejlhåndtering for at give informativ feedback til brugeren og forhindre applikationsnedbrud.
- Valider data grundigt: Udfør omfattende datavalidering for at sikre dataintegritet og forhindre sikkerhedssårbarheder.
- Sikre dine Server Actions: Implementer autentificerings- og autorisationsmekanismer for at beskytte følsomme data og funktionalitet.
- Optimer ydeevnen: Overvåg ydeevnen af dine Server Actions og optimer dem efter behov for at sikre hurtige svartider.
- Udnyt caching effektivt: Brug Next.js's cachingmekanismer til at forbedre ydeevnen og reducere databasebelastningen.
Almindelige faldgruber og hvordan man undgår dem
Selvom Server Actions tilbyder talrige fordele, er der nogle almindelige faldgruber, man skal være opmærksom på:
- Alt for komplekse Server Actions: Undgå at placere for meget logik i en enkelt Server Action. Opdel komplekse opgaver i mindre, mere håndterbare funktioner.
- Tilsidesættelse af fejlhåndtering: Inkluder altid fejlhåndtering for at fange uventede fejl og give nyttig feedback til brugeren.
- Ignorering af bedste praksisser for sikkerhed: Følg bedste praksisser for sikkerhed for at beskytte din applikation mod almindelige trusler som XSS og CSRF.
- Glemme at revalidere data: Sørg for at revalidere cachede data, efter en Server Action har ændret data, for at holde UI'en opdateret.
Konklusion
Next.js 14 Server Actions giver en kraftfuld og effektiv måde at håndtere formularindsendelser og datamutationer direkte på serveren. Ved at følge de bedste praksisser, der er beskrevet i denne guide, kan du bygge robuste, sikre og performante webapplikationer. Omfavn Server Actions for at forenkle din kode, forbedre sikkerheden og den samlede brugeroplevelse. Når du integrerer disse principper, skal du overveje den globale indvirkning af dine udviklingsvalg. Sørg for, at dine formularer og datahåndteringsprocesser er tilgængelige, sikre og brugervenlige for forskellige internationale målgrupper. Dette engagement i inklusivitet vil ikke kun forbedre din applikations anvendelighed, men også udvide dens rækkevidde og effektivitet på globalt plan.