Polski

Kompleksowy przewodnik po Next.js 14 Server Actions, obejmujący najlepsze praktyki obsługi formularzy, walidację danych, aspekty bezpieczeństwa i zaawansowane techniki budowania nowoczesnych aplikacji internetowych.

Next.js 14 Server Actions: Opanowanie najlepszych praktyk obsługi formularzy

Next.js 14 wprowadza potężne funkcje do budowania wydajnych i przyjaznych dla użytkownika aplikacji internetowych. Wśród nich Server Actions wyróżniają się jako transformacyjny sposób obsługi przesyłania formularzy i mutacji danych bezpośrednio na serwerze. Ten przewodnik zawiera kompleksowy przegląd Server Actions w Next.js 14, koncentrując się na najlepszych praktykach obsługi formularzy, walidacji danych, bezpieczeństwie i zaawansowanych technikach. Przeanalizujemy praktyczne przykłady i dostarczymy przydatnych informacji, które pomogą Ci budować solidne i skalowalne aplikacje internetowe.

Czym są Next.js Server Actions?

Server Actions to asynchroniczne funkcje, które działają na serwerze i mogą być wywoływane bezpośrednio z komponentów React. Eliminują one potrzebę tradycyjnych tras API do obsługi przesyłania formularzy i mutacji danych, co skutkuje uproszczeniem kodu, poprawą bezpieczeństwa i zwiększeniem wydajności. Server Actions to komponenty React Server Components (RSCs), co oznacza, że są wykonywane na serwerze, co prowadzi do szybszego ładowania początkowego strony i lepszego SEO.

Kluczowe korzyści Server Actions:

Konfiguracja projektu Next.js 14

Przed zagłębieniem się w Server Actions upewnij się, że masz skonfigurowany projekt Next.js 14. Jeśli zaczynasz od zera, utwórz nowy projekt za pomocą następującego polecenia:

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

Upewnij się, że Twój projekt korzysta ze struktury katalogów app, aby w pełni wykorzystać komponenty i akcje serwera.

Podstawowa obsługa formularzy za pomocą Server Actions

Zacznijmy od prostego przykładu: formularza, który przesyła dane w celu utworzenia nowego elementu w bazie danych. Użyjemy prostego formularza z polem wprowadzania i przyciskiem przesyłania.

Przykład: Tworzenie nowego elementu

Najpierw zdefiniuj funkcję Server Action w komponencie React. Ta funkcja będzie obsługiwać logikę przesyłania formularza na serwerze.

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

import { useState } from 'react';

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

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

  // Symulacja interakcji z bazą danych
  console.log('Tworzenie elementu:', name);

  await new Promise((resolve) => setTimeout(resolve, 1000)); // Symulacja opóźnienia

  console.log('Element utworzony pomyślnie!');
}

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

  return (
    
); }

Wyjaśnienie:

Walidacja danych

Walidacja danych ma kluczowe znaczenie dla zapewnienia integralności danych i zapobiegania lukom w zabezpieczeniach. Server Actions stanowią doskonałą okazję do przeprowadzenia walidacji po stronie serwera. Takie podejście pomaga ograniczyć ryzyko związane z samą walidacją po stronie klienta.

Przykład: Walidacja wprowadzanych danych

Zmodyfikuj Server Action createItem, aby uwzględnić logikę walidacji.

// 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('Nazwa elementu musi mieć co najmniej 3 znaki.');
  }

  // Symulacja interakcji z bazą danych
  console.log('Tworzenie elementu:', name);

  await new Promise((resolve) => setTimeout(resolve, 1000)); // Symulacja opóźnienia

  console.log('Element utworzony pomyślnie!');
}

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 || 'Wystąpił błąd.');
    } finally {
      setIsSubmitting(false);
    }
  }

  return (
    
{errorMessage &&

{errorMessage}

}
); }

Wyjaśnienie:

Korzystanie z bibliotek walidacyjnych

W przypadku bardziej złożonych scenariuszy walidacji rozważ użycie bibliotek walidacyjnych, takich jak:

Oto przykład użycia Zod:

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

export const CreateItemSchema = z.object({
  name: z.string().min(3, 'Nazwa elementu musi mieć co najmniej 3 znaki.'),
});
// 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 };
  }

  // Symulacja interakcji z bazą danych
  console.log('Tworzenie elementu:', name);

  await new Promise((resolve) => setTimeout(resolve, 1000)); // Symulacja opóźnienia

  console.log('Element utworzony pomyślnie!');
}

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 || 'Wystąpił błąd.');
    } finally {
      setIsSubmitting(false);
    }
  }

  return (
    
{errorMessage &&

{errorMessage}

}
); }

Wyjaśnienie:

Aspekty bezpieczeństwa

Server Actions zwiększają bezpieczeństwo, wykonując kod na serwerze, ale nadal kluczowe jest przestrzeganie najlepszych praktyk w zakresie bezpieczeństwa, aby chronić aplikację przed typowymi zagrożeniami.

Zapobieganie atakom Cross-Site Request Forgery (CSRF)

Ataki CSRF wykorzystują zaufanie, jakie witryna ma w przeglądarce użytkownika. Aby zapobiec atakom CSRF, zaimplementuj mechanizmy ochrony przed CSRF.

Next.js automatycznie obsługuje ochronę przed CSRF podczas korzystania z Server Actions. Framework generuje i weryfikuje token CSRF dla każdego przesłania formularza, zapewniając, że żądanie pochodzi z Twojej aplikacji.

Obsługa uwierzytelniania i autoryzacji użytkowników

Upewnij się, że tylko autoryzowani użytkownicy mogą wykonywać określone działania. Zaimplementuj mechanizmy uwierzytelniania i autoryzacji, aby chronić wrażliwe dane i funkcje.

Oto przykład użycia NextAuth.js do ochrony 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('Brak autoryzacji');
  }

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

  // Symulacja interakcji z bazą danych
  console.log('Tworzenie elementu:', name, 'przez użytkownika:', session.user?.email);

  await new Promise((resolve) => setTimeout(resolve, 1000)); // Symulacja opóźnienia

  console.log('Element utworzony pomyślnie!');
}

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 || 'Wystąpił błąd.');
    } finally {
      setIsSubmitting(false);
    }
  }

  return (
    
{errorMessage &&

{errorMessage}

}
); }

Wyjaśnienie:

Oczyszczanie wprowadzanych danych

Oczyść wprowadzane dane, aby zapobiec atakom Cross-Site Scripting (XSS). Ataki XSS mają miejsce, gdy złośliwy kod jest wstrzykiwany do witryny, potencjalnie naruszając dane użytkownika lub funkcjonalność aplikacji.

Używaj bibliotek takich jak DOMPurify lub sanitize-html, aby oczyścić dane wprowadzone przez użytkownika przed przetworzeniem ich w Server Actions.

Zaawansowane techniki

Teraz, gdy omówiliśmy podstawy, przyjrzyjmy się kilku zaawansowanym technikom efektywnego korzystania z Server Actions.

Optymistyczne aktualizacje

Optymistyczne aktualizacje zapewniają lepsze wrażenia użytkownika, natychmiast aktualizując interfejs użytkownika tak, jakby akcja miała się powieść, jeszcze zanim serwer to potwierdzi. Jeśli akcja nie powiedzie się na serwerze, interfejs użytkownika zostaje przywrócony do poprzedniego stanu.

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

  // Symulacja interakcji z bazą danych
  console.log('Aktualizowanie elementu:', id, 'z nazwą:', name);

  await new Promise((resolve) => setTimeout(resolve, 1000)); // Symulacja opóźnienia

  // Symulacja błędu (dla celów demonstracyjnych)
  const shouldFail = Math.random() < 0.5;
  if (shouldFail) {
    throw new Error('Nie udało się zaktualizować elementu.');
  }

  console.log('Element zaktualizowany pomyślnie!');
  return { name }; // Zwróć zaktualizowaną nazwę
}

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

    // Optymistyczna aktualizacja interfejsu użytkownika
    const newName = formData.get('name') as string;
    setItemName(newName);

    try {
      const result = await updateItem(id, formData);
      //Jeśli sukces, aktualizacja jest już odzwierciedlona w interfejsie użytkownika przez setItemName

    } catch (error: any) {
      setErrorMessage(error.message || 'Wystąpił błąd.');
      // Przywróć interfejs użytkownika w przypadku błędu
      setItemName(initialName);
    } finally {
      setIsSubmitting(false);
    }
  }

  return (
    

Aktualna nazwa: {itemName}

{errorMessage &&

{errorMessage}

}
); }

Wyjaśnienie:

Ponowna walidacja danych

Po tym, jak Server Action zmodyfikuje dane, może być konieczne ponowne zweryfikowanie danych w pamięci podręcznej, aby upewnić się, że interfejs użytkownika odzwierciedla najnowsze zmiany. Next.js udostępnia kilka sposobów ponownej walidacji danych:

Oto przykład ponownej walidacji ścieżki po utworzeniu nowego elementu:

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

  // Symulacja interakcji z bazą danych
  console.log('Tworzenie elementu:', name);

  await new Promise((resolve) => setTimeout(resolve, 1000)); // Symulacja opóźnienia

  console.log('Element utworzony pomyślnie!');

  revalidatePath('/items'); // Ponowna walidacja ścieżki /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 || 'Wystąpił błąd.');
    } finally {
      setIsSubmitting(false);
    }
  }

  return (
    
{errorMessage &&

{errorMessage}

}
); }

Wyjaśnienie:

Najlepsze praktyki dla Server Actions

Aby zmaksymalizować korzyści płynące z Server Actions, rozważ następujące najlepsze praktyki:

Typowe pułapki i jak ich unikać

Chociaż Server Actions oferują liczne zalety, należy pamiętać o kilku typowych pułapkach:

Wnioski

Next.js 14 Server Actions to potężny i wydajny sposób obsługi przesyłania formularzy i mutacji danych bezpośrednio na serwerze. Postępując zgodnie z najlepszymi praktykami opisanymi w tym przewodniku, możesz budować solidne, bezpieczne i wydajne aplikacje internetowe. Wykorzystaj Server Actions, aby uprościć kod, zwiększyć bezpieczeństwo i poprawić ogólne wrażenia użytkownika. Integrując te zasady, rozważ globalny wpływ wyborów programistycznych. Upewnij się, że Twoje formularze i procesy obsługi danych są dostępne, bezpieczne i przyjazne dla użytkownika dla zróżnicowanych odbiorców międzynarodowych. To zaangażowanie w inkluzywność nie tylko poprawi użyteczność Twojej aplikacji, ale także poszerzy jej zasięg i skuteczność w skali globalnej.