Logo NFU StudioNFU Logo icon
wróć do listy
Kodowe

/ Nextjs 14 - Server Actions

Nowy fajny i szybki mechanizm pobierania danych w Nextjs 14
Kacper Saweczko
4 minuty czytania

Next.js 14 Server Actions

czyli ciekawostka z gatunku przyrodniczych

NFU wyjaśnia - najpierw może trochę teorii

Next.js udostępnia programistom fajne techniki pobierania danych. Jedną z technik jest użycie Server Actions. Akcje serwerowe są oparte na akcjach React, które można definiować w komponentach serwerowych i/lub wywoływać z komponentów klienckich. Akcje serwera to asynchroniczne funkcje JavaScript uruchamiane na serwerze przez interakcje użytkownika na kliencie.

Jak używać Server Actions?

W komponentach serwerowych:

export default function OrderProducts() {
  async function addProductToOrder(product, orderId) {
    "use server";

    await saveProduct({ product, orderId });
  }

  return (
    <form action={addProductToOrder}>
      <button type="submit">Add Product</button>
    </form>
  );
}

w komponentach klienckich:

"use server";

import { addProductToOrder } from "@/data/order";

export async function addProduct(data, orderId) {
  const response = await addProductToOrder(data, orderId);
  return response;
}

Po co te całe Server Actions?

Dzięki Server Actions nie trzeba ręcznie tworzyć endpointów API. Zamiast tego definiuje się asynchroniczne funkcje serwera, które można wywoływać bezpośrednio z komponentów. W skrócie pozwalają zaoszczędzić mnóstwo cennego czasu na budowie funkcjonalności pobierania i wyświetlania danych. Zainteresowanych szczegółami odsyłamy do dokumentacji.

No to pora na jakiś przykład

Załóżmy, że pracujemy nad najbardziej powszechnym problemem dodawania i wyświetlania dodanych produktów. Mamy do stworzenia formularz dodawania produktów oraz sekcję istniejących produktów. Zacznijmy więc od formularza:

  <h1 className="text-center text-3xl font-bold">Products Warehouse</h1>
  <form action={""} className="mx-auto flex max-w-xl flex-col gap-5 p-5">
    <input
      name="product"
      placeholder="Enter Product name..."
      className="rounded-md border border-e-gray-300 p-2"
    ></input>
    <input
      name="price"
      placeholder="Enter Price name..."
      className="rounded-md border border-e-gray-300 p-2"
    ></input>
    <button
      type="submit"
      className="rounded-md border bg-blue-500 p-2 text-white"
    >
      Add Product
    </button>
  </form>

Formularz prezentuje się następująco:

form

Na potrzeby tego przykładu skorzystamy z Mockapi. Zróbmy schemat składający się z 3 pól: id, products i price:

Generate schema

następnie wygenerujmy dowolną ilość danych:

Generate data

Skoro mamy gotowe dane pora je wyświetlić. Potrzebny nam będzie do tego interfejs reprezentujący pola danych, funkcja pobierająca dane poprzez API, oraz mechanizm przerabiający otrzymany JSON na ciągi znaków i wyświetlający je użytkownikowi. Nie zapomnijmy o zmianie serwerowego komponentu na asynchroniczny!

  export interface Product {
    id?: number
    product: string
    price: string
  }

  export default async function Home() {
    const res = await fetch(
      'https://6565bfe0eb8bb4b70ef24984.mockapi.io/products',
      {
        cache: 'no-cache',
        next: {
          tags: ['products'],
        },
      }
    )
    const products: Product[] = await res.json()
    return (
    <h2 className='font-bold p-5 text-center mx-auto'>
      List of Products
    </h2>

    <div className='flex flex-wrap gap-5 max-w-xl mx-auto'>
      {products.map((product) => (
        <div key={product.id} className='p-5 shadow'>
          <p>{product.product}</p>
          <p>${product.price}</p>
        </div>
      ))}
    </div>
    )
  }

Po odświeżeniu strony powinniśmy zobaczyć pobrane dane:

data

Skoto mamy już możliość podejrzenia danych napiszmy mechanizm tworzący nową pozycję na liście. Tutaj pojawiają się właśnie Server Acions.

const addProductsToDatabase = async (e: FormData) => {
  "use server";
  const product = e.get("product")?.toString();
  const price = e.get("price")?.toString();

  if (!product || !price) return;

  const newProduct: Product = {
    product,
    price,
  };
  await fetch("https://6565bfe0eb8bb4b70ef24984.mockapi.io/products", {
    method: "POST",
    body: JSON.stringify(newProduct),
    headers: {
      "Content-Type": "application/json",
    },
  });
  revalidateTag("products");
};

następnie w akcji formularza:

<form
  action={addProductsToDatabase}
  className="mx-auto flex max-w-xl flex-col gap-5 p-5"
>
  (...)
</form>

Co się tutaj zadziało? Adnotacja 'use server' sugeruje, że jest to akcja serwera. W tym niewielkim kawałku kodu przechwycamy dane z formularza, a nastęnie metodą POST wysłamy na adres API. Zatwierdzając formularz wykonaliśmy na serwerze asynchroniczą funkcję addProductsToDatabase, która przetwarza dane przy pomocy interfejsu FormData, a następnie ustawia na ich podstawie elementy requestu. Dynamiczne odświeżenie listy produktów zawdzięczamy metodzie revalidateTag.

Teraz po wypełnieniu formularza mamy szybką odpowiedź w postaci zaaktualizowanej listy produktów:

final-list

Co z tego wynika?

Warto stosować Server Actions, w dużo krótszym czasie mamy zaimplementowane funkcje zastępujące endpointy. Jest to mechanizm wydajny pod względem czasu implementacji. Należy się łapka w górę. Dobra robota Vercel jak chcecie to umiecie :)

Co tu można poprawić?

Przykład pokazuje faktyczne zastosowanie Server Actions ale ze względu na to prostotę przykładu, pominęliśmy kilka ważnych aspektów. Chcesz potrenować? No to "dajemy wędkę":

  1. Zadbaj o bezpieczeństwo
    • dodaj walidację formularza po stronie klienta i serwera,
    • zapobiegnij XSS wprowadzając sanitację,
  2. Dodaj obsługę błędów przy żądaniach API
  3. Zadbaj o skalowalność:
    • rozdziel kod na mniejsze pliki zwiększając czytelność pod kątem rozwoju aplikacji
Kacper Saweczko

jeszcze jeden i starczy...

Projekty

/ Projekt strony internetowej - ważniejszy niż implementacja?

Po co jest faza projektowa strony internetowej i co powinno zostać ustalone przed rozpoczęciem implementacji. Jak projektować żeby to miało ręce i nogi a do tego przynosiło firmie zyski.
Kacper Saweczko
Przeczytaj post - Projekt strony internetowej - ważniejszy niż implementacja?

Design

/ Auto Layout w Figmie - wprowadzenie na dobry początek

Jeśli chcesz szybciej i przyjemniej projektować w Figmie to zapoznaj się jak używać Auto Layout.
Joanna Wasiluk
Przeczytaj post - Auto Layout w Figmie - wprowadzenie na dobry początek

Kodowe

/ Pojedynek Chatbot'ów - czyli nie tylko GPT

Krótkie porównanie największych chatbot'ów pod kątem użycia przez zwykłego Kowalskiego.
Kacper Saweczko
Przeczytaj post - Pojedynek Chatbot'ów - czyli nie tylko GPT