Действия (Actions)
Добавлено в: astro@4.15
Действия (Actions) в Astro позволяют определять и вызывать бэкенд-функции с полной типобезопасностью. Они берут на себя получение данных, парсинг JSON и валидацию входных параметров. Это значительно сокращает количество шаблонного кода по сравнению с использованием обычных API эндпойнтов.
Используйте действия вместо API эндпойнтов для удобного взаимодействия между клиентом и сервером:
- Автоматическая валидация JSON и данных форм с помощью Zod.
- Генерация типобезопасных функций для вызова бэкенда с клиента (и даже из HTML-форм). Больше не нужно вручную писать
fetch(). - Стандартизация ошибок бэкенда с помощью объекта
ActionError.
Базовое использование
Заголовок раздела «Базовое использование»Действия определяются в объекте server, который экспортируется из файла src/actions/index.ts:
import { defineAction } from 'astro:actions';import { z } from 'astro/zod';
export const server = { myAction: defineAction({ /* ... */ })}Ваши действия доступны как функции из модуля astro:actions. Импортируйте actions и вызывайте их на стороне клиента: в компонентах UI-фреймворков, в обработчиках форм или в тегах <script> компонентов Astro.
При вызове действие возвращает объект, содержащий либо data с результатом, либо error с возникшей ошибкой.
<script>import { actions } from 'astro:actions';
async () => { const { data, error } = await actions.myAction({ /* ... */ });}</script>Написание первого действия
Заголовок раздела «Написание первого действия»Выполните эти шаги, чтобы определить действие и вызвать его в теге script на странице Astro.
-
Создайте файл
src/actions/index.tsи экспортируйте объектserver. -
Импортируйте утилиту
defineAction()изastro:actionsи объектzизastro/zod. -
Используйте
defineAction()для определения действияgetGreeting. Свойствоinputиспользуется для валидации параметров через схему Zod, а функцияhandler()содержит логику, которая выполнится на сервере.src/actions/index.ts import { defineAction } from 'astro:actions';import { z } from 'astro/zod';export const server = {getGreeting: defineAction({input: z.object({name: z.string(),}),handler: async (input) => {return `Привет, ${input.name}!`}})} -
Создайте компонент Astro с кнопкой, которая при клике будет вызывать ваше действие.
src/pages/index.astro <button>Получить приветствие</button><script>import { actions } from 'astro:actions';const button = document.querySelector('button');button?.addEventListener('click', async () => {// Вызываем действие и получаем данныеconst { data, error } = await actions.getGreeting({ name: "Houston" });if (!error) alert(data);})</script>
defineAction() и его свойствах. Организация действий
Заголовок раздела «Организация действий»Все действия должны экспортироваться из объекта server в файле src/actions/index.ts. Вы можете определять их прямо там или выносить в отдельные файлы для удобства.
Например, действия пользователя можно вынести в src/actions/user.ts:
import { defineAction } from 'astro:actions';
export const user = { getUser: defineAction(/* ... */), createUser: defineAction(/* ... */),}Затем импортируйте этот объект в основной файл:
import { user } from './user';
export const server = { myAction: defineAction({ /* ... */ }), user,}Теперь действия доступны через actions.user.getUser() и actions.user.createUser().
Обработка результатов
Заголовок раздела «Обработка результатов»Действия возвращают объект с типобезопасным результатом в data или ошибкой в error. Ошибки могут возникнуть как на этапе валидации input, так и внутри самого handler().
Astro использует библиотеку Devalue для передачи данных, поэтому действия поддерживают передачу объектов Date, Map, Set и URL.
Проверка на наличие ошибок
Заголовок раздела «Проверка на наличие ошибок»Рекомендуется проверять error перед использованием data. Это гарантирует, что данные определены.
const { data, error } = await actions.example();
if (error) { // обработка ошибок return;}// работа с dataИспользование .orThrow()
Заголовок раздела «Использование .orThrow()»Чтобы пропустить ручную проверку ошибок (например, при прототипировании), используйте метод .orThrow(). Он выбросит исключение в случае ошибки бэкенда и вернет data напрямую.
const updatedLikes = await actions.likePost.orThrow({ postId: 'example' });// ^ тип: numberГенерация ошибок на бэкенде
Заголовок раздела «Генерация ошибок на бэкенде»Вы можете использовать класс ActionError, чтобы сообщить о проблеме (например, «не найдено» или «не авторизован»). Это лучше, чем просто возвращать undefined, так как позволяет задать HTTP-статус.
import { defineAction, ActionError } from "astro:actions";
export const server = { likePost: defineAction({ handler: async (input, ctx) => { if (!ctx.cookies.has('user-session')) { throw new ActionError({ code: "UNAUTHORIZED", message: "Вы должны войти в систему.", }); } }, }),};Работа с формами
Заголовок раздела «Работа с формами»По умолчанию действия принимают JSON. Чтобы принимать данные из HTML-формы, установите accept: 'form':
export const server = { comment: defineAction({ accept: 'form', input: z.object({ author: z.string(), content: z.string(), }), handler: async (input) => { /* ... */ }, })}При отправке формы через actions.comment(formData), Astro автоматически преобразует FormData в объект для валидации.
Безопасность
Заголовок раздела «Безопасность»Действия доступны как публичные эндпойнты (например, /_actions/myAction). Поэтому в них обязательно нужно проводить те же проверки авторизации, что и в обычных API.
Вы можете проверять права доступа прямо в handler, используя context.locals, которые передаются из мидлваров:
export const server = { getUserSettings: defineAction({ handler: async (_input, context) => { if (!context.locals.user) { throw new ActionError({ code: 'UNAUTHORIZED' }); } return { /* данные */ }; } })}