Українська

Вичерпний посібник із серверних дій в Next.js 14, що охоплює найкращі практики обробки форм, валідацію даних, аспекти безпеки та передові техніки для створення сучасних вебдодатків.

Серверні дії в Next.js 14: Опанування найкращих практик обробки форм

Next.js 14 представляє потужні функції для створення продуктивних та зручних для користувача вебдодатків. Серед них Серверні дії (Server Actions) виділяються як трансформаційний спосіб обробки надсилань форм та мутацій даних безпосередньо на сервері. Цей посібник надає вичерпний огляд Серверних дій у Next.js 14, зосереджуючись на найкращих практиках обробки форм, валідації даних, безпеки та передових техніках. Ми розглянемо практичні приклади та надамо дієві поради, які допоможуть вам створювати надійні та масштабовані вебдодатки.

Що таке Серверні дії в Next.js?

Серверні дії — це асинхронні функції, які виконуються на сервері та можуть викликатися безпосередньо з компонентів React. Вони усувають потребу в традиційних API-маршрутах для обробки надсилань форм та мутацій даних, що призводить до спрощення коду, покращення безпеки та підвищення продуктивності. Серверні дії є Серверними компонентами React (RSC), що означає, що вони виконуються на сервері, забезпечуючи швидше початкове завантаження сторінки та покращене SEO.

Ключові переваги Серверних дій:

Налаштування вашого проєкту Next.js 14

Перш ніж занурюватися в Серверні дії, переконайтеся, що у вас налаштовано проєкт Next.js 14. Якщо ви починаєте з нуля, створіть новий проєкт за допомогою наступної команди:

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

Переконайтеся, що ваш проєкт використовує структуру директорій app, щоб повною мірою скористатися перевагами Серверних компонентів та дій.

Базова обробка форм за допомогою Серверних дій

Почнемо з простого прикладу: форма, яка надсилає дані для створення нового елемента в базі даних. Ми будемо використовувати просту форму з полем вводу та кнопкою надсилання.

Приклад: Створення нового елемента

Спочатку визначте функцію Серверної дії у вашому компоненті React. Ця функція буде обробляти логіку надсилання форми на сервері.

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

import { useState } from 'react';

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

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

  // Симуляція взаємодії з базою даних
  console.log('Створення елемента:', name);

  await new Promise((resolve) => setTimeout(resolve, 1000)); // Симуляція затримки

  console.log('Елемент успішно створено!');
}

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

  return (
    
); }

Пояснення:

Валідація даних

Валідація даних є надзвичайно важливою для забезпечення цілісності даних та запобігання вразливостям безпеки. Серверні дії надають чудову можливість для виконання валідації на стороні сервера. Цей підхід допомагає зменшити ризики, пов'язані лише з валідацією на стороні клієнта.

Приклад: Валідація вхідних даних

Змініть Серверну дію createItem, щоб додати логіку валідації.

// 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('Назва елемента повинна містити щонайменше 3 символи.');
  }

  // Симуляція взаємодії з базою даних
  console.log('Створення елемента:', name);

  await new Promise((resolve) => setTimeout(resolve, 1000)); // Симуляція затримки

  console.log('Елемент успішно створено!');
}

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 || 'Сталася помилка.');
    } finally {
      setIsSubmitting(false);
    }
  }

  return (
    
{errorMessage &&

{errorMessage}

}
); }

Пояснення:

Використання бібліотек для валідації

Для складніших сценаріїв валідації розгляньте можливість використання таких бібліотек, як:

Ось приклад використання Zod:

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

export const CreateItemSchema = z.object({
  name: z.string().min(3, 'Назва елемента повинна містити щонайменше 3 символи.'),
});
// 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 };
  }

  // Симуляція взаємодії з базою даних
  console.log('Створення елемента:', name);

  await new Promise((resolve) => setTimeout(resolve, 1000)); // Симуляція затримки

  console.log('Елемент успішно створено!');
}

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 || 'Сталася помилка.');
    } finally {
      setIsSubmitting(false);
    }
  }

  return (
    
{errorMessage &&

{errorMessage}

}
); }

Пояснення:

Аспекти безпеки

Серверні дії підвищують безпеку, виконуючи код на сервері, але все одно важливо дотримуватися найкращих практик безпеки для захисту вашого додатку від поширених загроз.

Запобігання міжсайтовій підробці запитів (CSRF)

CSRF-атаки використовують довіру, яку вебсайт має до браузера користувача. Щоб запобігти CSRF-атакам, впроваджуйте механізми захисту від CSRF.

Next.js автоматично обробляє захист від CSRF при використанні Серверних дій. Фреймворк генерує та перевіряє CSRF-токен для кожного надсилання форми, забезпечуючи, що запит походить саме з вашого додатку.

Обробка автентифікації та авторизації користувачів

Переконайтеся, що лише авторизовані користувачі можуть виконувати певні дії. Впроваджуйте механізми автентифікації та авторизації для захисту конфіденційних даних та функціональності.

Ось приклад використання NextAuth.js для захисту Серверної дії:

// 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('Неавторизований');
  }

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

  // Симуляція взаємодії з базою даних
  console.log('Створення елемента:', name, 'користувачем:', session.user?.email);

  await new Promise((resolve) => setTimeout(resolve, 1000)); // Симуляція затримки

  console.log('Елемент успішно створено!');
}

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 || 'Сталася помилка.');
    } finally {
      setIsSubmitting(false);
    }
  }

  return (
    
{errorMessage &&

{errorMessage}

}
); }

Пояснення:

Санітизація вхідних даних

Санітизуйте вхідні дані, щоб запобігти атакам міжсайтового скриптингу (XSS). XSS-атаки відбуваються, коли шкідливий код впроваджується на вебсайт, потенційно компрометуючи дані користувачів або функціональність додатку.

Використовуйте бібліотеки, такі як DOMPurify або sanitize-html, для санітизації даних, наданих користувачем, перед їх обробкою у ваших Серверних діях.

Передові техніки

Тепер, коли ми розглянули основи, давайте дослідимо деякі передові техніки для ефективного використання Серверних дій.

Оптимістичні оновлення

Оптимістичні оновлення забезпечують кращий досвід користувача, негайно оновлюючи інтерфейс так, ніби дія буде успішною, ще до того, як сервер це підтвердить. Якщо дія на сервері не вдається, інтерфейс повертається до свого попереднього стану.

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

  // Симуляція взаємодії з базою даних
  console.log('Оновлення елемента:', id, 'з назвою:', name);

  await new Promise((resolve) => setTimeout(resolve, 1000)); // Симуляція затримки

  // Симуляція невдачі (для демонстраційних цілей)
  const shouldFail = Math.random() < 0.5;
  if (shouldFail) {
    throw new Error('Не вдалося оновити елемент.');
  }

  console.log('Елемент успішно оновлено!');
  return { 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);

    // Оптимістично оновлюємо інтерфейс
    const newName = formData.get('name') as string;
    setItemName(newName);

    try {
      const result = await updateItem(id, formData);
      //Якщо успішно, оновлення вже відображено в інтерфейсі через setItemName

    } catch (error: any) {
      setErrorMessage(error.message || 'Сталася помилка.');
      // Повертаємо інтерфейс до початкового стану при помилці
      setItemName(initialName);
    } finally {
      setIsSubmitting(false);
    }
  }

  return (
    

Поточна назва: {itemName}

{errorMessage &&

{errorMessage}

}
); }

Пояснення:

Ревалідація даних

Після того, як Серверна дія змінює дані, вам може знадобитися ревалідувати кешовані дані, щоб інтерфейс відображав останні зміни. Next.js надає кілька способів ревалідації даних:

Ось приклад ревалідації шляху після створення нового елемента:

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

  // Симуляція взаємодії з базою даних
  console.log('Створення елемента:', name);

  await new Promise((resolve) => setTimeout(resolve, 1000)); // Симуляція затримки

  console.log('Елемент успішно створено!');

  revalidatePath('/items'); // Ревалідувати шлях /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 || 'Сталася помилка.');
    } finally {
      setIsSubmitting(false);
    }
  }

  return (
    
{errorMessage &&

{errorMessage}

}
); }

Пояснення:

Найкращі практики для Серверних дій

Щоб максимізувати переваги Серверних дій, дотримуйтесь таких найкращих практик:

Поширені помилки та як їх уникнути

Хоча Серверні дії пропонують численні переваги, є деякі поширені помилки, про які варто знати:

Висновок

Серверні дії в Next.js 14 надають потужний та ефективний спосіб обробки надсилань форм та мутацій даних безпосередньо на сервері. Дотримуючись найкращих практик, викладених у цьому посібнику, ви зможете створювати надійні, безпечні та продуктивні вебдодатки. Використовуйте Серверні дії, щоб спростити свій код, підвищити безпеку та покращити загальний досвід користувача. Інтегруючи ці принципи, враховуйте глобальний вплив ваших рішень у розробці. Переконайтеся, що ваші форми та процеси обробки даних є доступними, безпечними та зручними для різноманітних міжнародних аудиторій. Ця прихильність до інклюзивності не лише покращить юзабіліті вашого додатку, але й розширить його охоплення та ефективність у глобальному масштабі.