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:
- Förenklad kod: Minska standardkod genom att eliminera behovet av separata API-rutter.
- Förbättrad säkerhet: Exekvering på serversidan minimerar sårbarheter på klientsidan.
- Förbättrad prestanda: Utför datamutationer direkt på servern för snabbare svarstider.
- Optimerad SEO: Utnyttja server-side rendering för bättre indexering av sökmotorer.
- Typsäkerhet: Dra nytta av end-to-end typsäkerhet med TypeScript.
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:
- Direktivet
'use client'
indikerar att detta är en klientkomponent. - Funktionen
createItem
är markerad med direktivet'use server'
, vilket indikerar att det är en Server Action. - Funktionen
handleSubmit
är en klientsidefunktion som anropar serveråtgärden. Den hanterar också UI-tillstånd som att inaktivera knappen under inskickning. <form>
-elementetsaction
-prop är satt tillhandleSubmit
-funktionen.- Metoden
formData.get('name')
hämtar värdet från 'name'-inmatningsfältet. await new Promise
simulerar en databasoperation och lägger till latens.
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:
- Funktionen
createItem
kontrollerar nu omname
är giltigt (minst 3 tecken långt). - Om valideringen misslyckas kastas ett fel.
- Funktionen
handleSubmit
är uppdaterad för att fånga eventuella fel som kastas av Server Action och visa ett felmeddelande för användaren.
Använda valideringsbibliotek
För mer komplexa valideringsscenarier, överväg att använda valideringsbibliotek som:
- Zod: Ett TypeScript-first schema-deklarations- och valideringsbibliotek.
- Yup: En JavaScript-schemabyggare för att tolka, validera och transformera värden.
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:
CreateItemSchema
definierar valideringsreglerna förname
-fältet med Zod.- Metoden
safeParse
försöker validera indata. Om valideringen misslyckas returnerar den ett objekt med felen. - Objektet
errors
innehåller detaljerad information om valideringsfelen.
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:
- Funktionen
getServerSession
hämtar användarens sessionsinformation. - Om användaren inte är autentiserad (ingen session), kastas ett fel, vilket förhindrar att Server Action exekveras.
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:
- Innan Server Action anropas uppdateras gränssnittet omedelbart med det nya objektnamnet med hjälp av
setItemName
. - Om Server Action misslyckas återställs gränssnittet till det ursprungliga objektnamnet.
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:
- Revalidate Path: Revalidera cachen för en specifik sökväg.
- Revalidate Tag: Revalidera cachen för data som är associerad med en specifik tagg.
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:
- Funktionen
revalidatePath('/items')
invaliderar cachen för sökvägen/items
, vilket säkerställer att nästa begäran till den sökvägen hämtar de senaste data.
Bästa praxis för Server Actions
För att maximera fördelarna med Server Actions, överväg följande bästa praxis:
- Håll Server Actions små och fokuserade: Server Actions bör utföra en enda, väldefinierad uppgift. Undvik komplex logik inom Server Actions för att bibehålla läsbarhet och testbarhet.
- Använd beskrivande namn: Ge dina Server Actions beskrivande namn som tydligt indikerar deras syfte.
- Hantera fel elegant: Implementera robust felhantering för att ge informativ feedback till användaren och förhindra applikationskrascher.
- Validera data noggrant: Utför omfattande datavalidering för att säkerställa dataintegritet och förhindra säkerhetssårbarheter.
- Säkra dina Server Actions: Implementera autentiserings- och auktoriseringsmekanismer för att skydda känsliga data och funktioner.
- Optimera prestanda: Övervaka prestandan för dina Server Actions och optimera dem vid behov för att säkerställa snabba svarstider.
- Använd cachelagring effektivt: Utnyttja Next.js cachemekanismer för att förbättra prestanda och minska databasbelastningen.
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:
- Alltför komplexa Server Actions: Undvik att lägga för mycket logik i en enda Server Action. Bryt ner komplexa uppgifter i mindre, mer hanterbara funktioner.
- Ignorera felhantering: Inkludera alltid felhantering för att fånga oväntade fel och ge användbar feedback till användaren.
- Ignorera bästa praxis för säkerhet: Följ bästa praxis för säkerhet för att skydda din applikation från vanliga hot som XSS och CSRF.
- Glömma att revalidera data: Se till att du revaliderar cachad data efter att en Server Action har modifierat data för att hålla gränssnittet uppdaterat.
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.