Załóżmy, że w Twoim projekcie istnieje już baza danych. Sama baza danych to jednak za mało, jeśli użytkownik nie ma możliwości zapisania komentarza, wysłania zgłoszenia czy zmiany ustawień. Ręczne tworzenie endpointów REST jest oczywiście możliwe, ale w wielu przypadkach generuje zbyt wiele zbędnego kodu. Właśnie tutaj z pomocą przychodzą Astro Actions, które znacząco skracają drogę od formularza do logiki serwerowej.
Jaki problem rozwiązują Astro Actions w formularzach i mutacjach danych
Wyobraź sobie typowy formularz komentarza. W podejściu z ręcznym endpointem przechodzisz przez cztery kroki, w których łatwo o błąd:
- Endpoint (
src/pages/api/comment.ts), czyli sam wywołujeszrequest.json()alborequest.formData(). - Walidacja, w której sprawdzasz pola ręcznie albo dokładasz Zod i obsługujesz wynik.
- Odpowiedź, czyli składasz
Responsez odpowiednim statusem HTTP i ciałem. - Klient, czyli piszesz
fetch('/api/comment'), obsługujesz błędy, a typy odpowiedzi przepisujesz ręcznie albo współdzielisz w osobnym module.
Bez wspólnego kontraktu między krokiem trzecim a czwartym TypeScript przestaje pomagać, bo serwer wie, co zwraca, ale klient już nie. Astro Actions automatycznie tworzą taki kontrakt i wystarczy, że raz zdefiniujesz logikę, a typ wyniku zostanie przekazany do wygenerowanego klienta.
Jak zdefiniować Astro Action w src/actions/index.ts
Wszystkie akcje eksportujesz jako obiekt server z pliku src/actions/index.ts, a każdą pojedynczą akcję opakowujesz w :
Zachowanie akcji określają trzy pola:
accept: 'form', czyli akcja przyjmujeFormData— dlatego tę samą definicję wywołasz z formularza HTML oraz z kodu klienckiego przekazującegoFormData.input, czyli schemat , którym Astro waliduje dane jeszcze zanim wywoła handler. Jeśli wejście nie pasuje do schematu, handler w ogóle się nie uruchomi.handler, czyli logika serwerowa. Argument jest już zwalidowany i otypowany zgodnie ze schemateminput, więc piszesz kod biznesowy bez defensywnych sprawdzeń.
Jak wywołać Astro Action z klienta jak zwykłą funkcję
Z komponentu klienckiego (React, Vue, Svelte albo <script>) importujesz akcje z i wywołujesz jak zwykłą funkcję. Ponieważ addComment ma accept: 'form', przekazujesz jej FormData:
data ma typ dokładnie odpowiadający temu, co zwraca handler ({ id, status }) i to bez ręcznego deklarowania interfejsu odpowiedzi. Zmiana wyniku akcji zostanie więc wykryta w komponencie przez TypeScript. Przy accept: 'form' wejściem po stronie klienta pozostaje jednak FormData; pełne typowanie obiektu wejściowego otrzymasz w akcji z domyślnym accept: 'json'.
Klasyczne formularze HTML w Astro Actions z accept: 'form'
Wywołanie RPC wymaga JavaScriptu po stronie klienta. Ta sama akcja z accept: 'form' obsłuży również formularz bez kodu klienckiego, gdy przypiszesz ją do atrybutu action formularza. Pełny przykład wraz z obsługą wyniku znajduje się poniżej.
Wynik formularza bez JavaScriptu: getActionResult i wzorzec PRG
Formularz działa, ale chcemy, by użytkownik widział, co się stało. Przy wysyłce bez JavaScriptu brakuje jednak funkcji setStatus. Od tego jest Astro.getActionResult(): po POST-cie strona renderuje się ponownie i we frontmatterze masz dostęp do wyniku akcji:
Po udanym wykonaniu akcji przekierowanie realizuje wzorzec POST-Redirect-GET. Kod 303 informuje przeglądarkę, że po żądaniu POST powinna pobrać stronę metodą GET, dzięki czemu odświeżenie nie spowoduje ponownego wysłania komentarza. Błędy walidacji pozostają na stronie, a error.fields z isInputError przypisujesz do odpowiednich pól, analogicznie jak w wariancie RPC.
Akcję można uruchomić również z poziomu kodu serwerowego strony za pomocą Astro.callAction(actions.addComment, formData). W przypadku akcji z accept: 'form' drugim argumentem nadal musi być FormData. W endpoincie serwerowym odpowiednikiem tej funkcji jest context.callAction().
Obsługa błędów w Astro Actions: { data, error } lub .orThrow()
Standardowe wywołanie Astro Action zwraca obiekt { data, error }, dzięki czemu obsługujesz niepowodzenie jawnie, a jeśli w danym miejscu wygodniejszy będzie try/catch, możesz wywołać actions.addComment.orThrow(formData). Ten wariant zwraca bezpośrednio dane albo rzuca wyjątek.
Błędy dzielą się na dwie kategorie:
- rozpoznaje błąd walidacji Zod.
error.fieldszawiera komunikaty dla poszczególnych pól, gotowe do wyświetlenia przy kontrolkach formularza. - rzucasz go z handlera dla błędów biznesowych, z kodem statusu zamiast surowego HTTP:
Astro Actions czy endpointy serwerowe: kiedy wybrać które rozwiązanie
Astro Actions nie zastępują endpointów całkowicie, o czym poniżej.
| Potrzeba | Wybór |
|---|---|
| Mutacja danych z własnej aplikacji (formularz, przycisk, akcja użytkownika) | Astro Actions — typowanie, walidacja, mniej kodu |
| Publiczne API REST dla zewnętrznych klientów | Endpoint serwerowy — pełna kontrola nad ścieżką, metodą i nagłówkami |
| Webhook przyjmujący żądania spoza Twojej aplikacji | Endpoint serwerowy — Actions zakładają własnego klienta |
| Zwracanie pliku, strumienia lub własnej odpowiedzi HTTP | Endpoint serwerowy |
Pamiętaj, by wewnątrz własnej aplikacji wybierać Actions, a dla klientów zewnętrznych — endpoint serwerowy.
Bezpieczeństwo Astro Actions i dobre praktyki wdrożenia
- Walidacja nie jest autoryzacją.
inputZod sprawdza kształt danych, ale nie uprawnienia. Sprawdzenie, czy użytkownik może wykonać akcję, robisz w handlerze przezcontext.localsiActionError. - Nie ufaj polom ukrytym.
postSlugz<input type="hidden">może zostać podmieniony. Waliduj, że odnosi się do realnego, dozwolonego zasobu. - Obsłuż
errorpo stronie wywołującego. Przy standardowym wywołaniu pominięcie sprawdzeniaerroroznacza ciche niepowodzenie. Używając w sposób przemyślany.orThrow(), przechwycisz wyjątek wtedy, kiedy trzeba. - Ogranicz liczbę zgłoszeń. Publiczne akcje, np. komentarze lub formularze kontaktowe, potrzebują limitu wywołań egzekwowanych w funkcji
handlerlub w middleware. - Nie wyłączaj ochrony origin bez konkretnego powodu.
security.checkOrigindomyślnie chroni renderowane na żądanie formularze przed CSRF dla obsługiwanych typów treści. Nie obejmuje jednak każdego możliwego klienta i nie zastąpi autoryzacji.
Miejsce Astro Actions w nowoczesnym stosie Astro
Astro Actions spinają warstwę danych z interfejsem:
- Mutacja danych w bazie (np. libSQL/Turso przez Drizzle) odbywa się bezpośrednio: od formularza, poprzez akcję, do
db.insert, bez konieczności użycia pośredniego endpointu. - Wywołujesz je z wysp interaktywnych (React/Vue/Svelte) albo z czystego formularza HTML.
- Middleware może odczytać sesję i umieścić użytkownika w
context.locals, ale uprawnienia do wykonania mutacji sprawdza samhandler. Budowę tej warstwy opisuję w artykule o middleware i autoryzacji w Astro.
