Hrvatski

Sveobuhvatan vodič za Next.js 14 poslužiteljske akcije, koji pokriva najbolje prakse rukovanja obrascima, validaciju podataka, sigurnosna razmatranja i napredne tehnike za izradu modernih web aplikacija.

Next.js 14 poslužiteljske akcije: Ovladavanje najboljim praksama za rukovanje obrascima

Next.js 14 uvodi moćne značajke za izradu performantnih i korisnički prihvatljivih web aplikacija. Među njima se ističu poslužiteljske akcije (Server Actions) kao transformativan način rukovanja podnošenjem obrazaca i mutacijama podataka izravno na poslužitelju. Ovaj vodič pruža sveobuhvatan pregled poslužiteljskih akcija u Next.js 14, s naglaskom na najbolje prakse za rukovanje obrascima, validaciju podataka, sigurnost i napredne tehnike. Istražit ćemo praktične primjere i pružiti primjenjive uvide koji će vam pomoći u izgradnji robusnih i skalabilnih web aplikacija.

Što su Next.js poslužiteljske akcije?

Poslužiteljske akcije su asinkrone funkcije koje se izvršavaju na poslužitelju i mogu se pozvati izravno iz React komponenata. One eliminiraju potrebu za tradicionalnim API rutama za rukovanje podnošenjem obrazaca i mutacijama podataka, što rezultira pojednostavljenim kodom, poboljšanom sigurnošću i unaprijeđenim performansama. Poslužiteljske akcije su React poslužiteljske komponente (React Server Components - RSCs), što znači da se izvršavaju na poslužitelju, dovodeći do bržeg početnog učitavanja stranica i boljeg SEO-a.

Ključne prednosti poslužiteljskih akcija:

Postavljanje vašeg Next.js 14 projekta

Prije nego što zaronite u poslužiteljske akcije, provjerite imate li postavljen Next.js 14 projekt. Ako počinjete od nule, kreirajte novi projekt koristeći sljedeću naredbu:

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

Pobrinite se da vaš projekt koristi app strukturu direktorija kako biste u potpunosti iskoristili poslužiteljske komponente i akcije.

Osnovno rukovanje obrascima s poslužiteljskim akcijama

Krenimo s jednostavnim primjerom: obrazac koji podnosi podatke za stvaranje nove stavke u bazi podataka. Koristit ćemo jednostavan obrazac s jednim poljem za unos i gumbom za podnošenje.

Primjer: Stvaranje nove stavke

Prvo, definirajte funkciju poslužiteljske akcije unutar vaše React komponente. Ova funkcija će rukovati logikom podnošenja obrasca na poslužitelju.

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

import { useState } from 'react';

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

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

  // Simulacija interakcije s bazom podataka
  console.log('Stvaranje stavke:', name);

  await new Promise((resolve) => setTimeout(resolve, 1000)); // Simulacija latencije

  console.log('Stavka uspješno stvorena!');
}

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

  return (
    
); }

Objašnjenje:

Validacija podataka

Validacija podataka ključna je za osiguravanje integriteta podataka i sprječavanje sigurnosnih ranjivosti. Poslužiteljske akcije pružaju izvrsnu priliku za provođenje validacije na strani poslužitelja. Ovaj pristup pomaže u ublažavanju rizika povezanih isključivo s validacijom na strani klijenta.

Primjer: Validacija ulaznih podataka

Izmijenite poslužiteljsku akciju createItem kako biste uključili logiku validacije.

// 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('Naziv stavke mora imati najmanje 3 znaka.');
  }

  // Simulacija interakcije s bazom podataka
  console.log('Stvaranje stavke:', name);

  await new Promise((resolve) => setTimeout(resolve, 1000)); // Simulacija latencije

  console.log('Stavka uspješno stvorena!');
}

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 || 'Došlo je do pogreške.');
    } finally {
      setIsSubmitting(false);
    }
  }

  return (
    
{errorMessage &&

{errorMessage}

}
); }

Objašnjenje:

Korištenje biblioteka za validaciju

Za složenije scenarije validacije, razmislite o korištenju biblioteka za validaciju kao što su:

Evo primjera korištenja Zoda:

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

export const CreateItemSchema = z.object({
  name: z.string().min(3, 'Naziv stavke mora imati najmanje 3 znaka.'),
});
// 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 };
  }

  // Simulacija interakcije s bazom podataka
  console.log('Stvaranje stavke:', name);

  await new Promise((resolve) => setTimeout(resolve, 1000)); // Simulacija latencije

  console.log('Stavka uspješno stvorena!');
}

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 || 'Došlo je do pogreške.');
    } finally {
      setIsSubmitting(false);
    }
  }

  return (
    
{errorMessage &&

{errorMessage}

}
); }

Objašnjenje:

Sigurnosna razmatranja

Poslužiteljske akcije poboljšavaju sigurnost izvršavanjem koda na poslužitelju, ali i dalje je ključno slijediti najbolje sigurnosne prakse kako biste zaštitili svoju aplikaciju od uobičajenih prijetnji.

Sprječavanje Cross-Site Request Forgery (CSRF) napada

CSRF napadi iskorištavaju povjerenje koje web stranica ima u korisnikov preglednik. Kako biste spriječili CSRF napade, implementirajte mehanizme za zaštitu od CSRF-a.

Next.js automatski rukuje zaštitom od CSRF-a kada se koriste poslužiteljske akcije. Framework generira i provjerava CSRF token za svako podnošenje obrasca, osiguravajući da zahtjev potječe iz vaše aplikacije.

Rukovanje autentifikacijom i autorizacijom korisnika

Osigurajte da samo autorizirani korisnici mogu izvršavati određene akcije. Implementirajte mehanizme za autentifikaciju i autorizaciju kako biste zaštitili osjetljive podatke i funkcionalnosti.

Evo primjera korištenja NextAuth.js za zaštitu poslužiteljske akcije:

// 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('Neautorizirano');
  }

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

  // Simulacija interakcije s bazom podataka
  console.log('Stvaranje stavke:', name, 'od strane korisnika:', session.user?.email);

  await new Promise((resolve) => setTimeout(resolve, 1000)); // Simulacija latencije

  console.log('Stavka uspješno stvorena!');
}

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 || 'Došlo je do pogreške.');
    } finally {
      setIsSubmitting(false);
    }
  }

  return (
    
{errorMessage &&

{errorMessage}

}
); }

Objašnjenje:

Sanitizacija ulaznih podataka

Sanitizirajte ulazne podatke kako biste spriječili Cross-Site Scripting (XSS) napade. XSS napadi se događaju kada se zlonamjerni kod ubaci na web stranicu, potencijalno ugrožavajući korisničke podatke ili funkcionalnost aplikacije.

Koristite biblioteke poput DOMPurify ili sanitize-html za sanitizaciju korisničkih unosa prije njihove obrade u vašim poslužiteljskim akcijama.

Napredne tehnike

Sada kada smo pokrili osnove, istražimo neke napredne tehnike za učinkovito korištenje poslužiteljskih akcija.

Optimistična ažuriranja

Optimistična ažuriranja pružaju bolje korisničko iskustvo trenutnim ažuriranjem korisničkog sučelja kao da će akcija uspjeti, čak i prije nego što poslužitelj to potvrdi. Ako akcija ne uspije na poslužitelju, korisničko sučelje se vraća u prethodno stanje.

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

  // Simulacija interakcije s bazom podataka
  console.log('Ažuriranje stavke:', id, 's nazivom:', name);

  await new Promise((resolve) => setTimeout(resolve, 1000)); // Simulacija latencije

  // Simulacija neuspjeha (za demonstracijske svrhe)
  const shouldFail = Math.random() < 0.5;
  if (shouldFail) {
    throw new Error('Ažuriranje stavke nije uspjelo.');
  }

  console.log('Stavka uspješno ažurirana!');
  return { name }; // Vrati ažurirani naziv
}

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

    // Optimistično ažuriraj korisničko sučelje
    const newName = formData.get('name') as string;
    setItemName(newName);

    try {
      const result = await updateItem(id, formData);
      //Ako je uspješno, ažuriranje je već vidljivo u sučelju putem setItemName

    } catch (error: any) {
      setErrorMessage(error.message || 'Došlo je do pogreške.');
      // Vrati korisničko sučelje u prethodno stanje u slučaju pogreške
      setItemName(initialName);
    } finally {
      setIsSubmitting(false);
    }
  }

  return (
    

Trenutni naziv: {itemName}

{errorMessage &&

{errorMessage}

}
); }

Objašnjenje:

Ponovna validacija podataka

Nakon što poslužiteljska akcija izmijeni podatke, možda ćete morati ponovno validirati keširane podatke kako biste osigurali da korisničko sučelje odražava najnovije promjene. Next.js pruža nekoliko načina za ponovnu validaciju podataka:

Evo primjera ponovne validacije putanje nakon stvaranja nove stavke:

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

  // Simulacija interakcije s bazom podataka
  console.log('Stvaranje stavke:', name);

  await new Promise((resolve) => setTimeout(resolve, 1000)); // Simulacija latencije

  console.log('Stavka uspješno stvorena!');

  revalidatePath('/items'); // Ponovno validiraj putanju /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 || 'Došlo je do pogreške.');
    } finally {
      setIsSubmitting(false);
    }
  }

  return (
    
{errorMessage &&

{errorMessage}

}
); }

Objašnjenje:

Najbolje prakse za poslužiteljske akcije

Kako biste maksimalno iskoristili prednosti poslužiteljskih akcija, razmotrite sljedeće najbolje prakse:

Uobičajene zamke i kako ih izbjeći

Iako poslužiteljske akcije nude brojne prednosti, postoje neke uobičajene zamke kojih treba biti svjestan:

Zaključak

Next.js 14 poslužiteljske akcije pružaju moćan i učinkovit način za rukovanje podnošenjem obrazaca i mutacijama podataka izravno na poslužitelju. Slijedeći najbolje prakse navedene u ovom vodiču, možete izgraditi robusne, sigurne i performantne web aplikacije. Prihvatite poslužiteljske akcije kako biste pojednostavili svoj kod, poboljšali sigurnost i unaprijedili cjelokupno korisničko iskustvo. Dok integrirate ove principe, razmislite o globalnom utjecaju svojih razvojnih odabira. Osigurajte da su vaši obrasci i procesi rukovanja podacima pristupačni, sigurni i jednostavni za korištenje raznolikoj međunarodnoj publici. Ova predanost inkluzivnosti ne samo da će poboljšati upotrebljivost vaše aplikacije, već će i proširiti njezin doseg i učinkovitost na globalnoj razini.