33. Astro Actions
Astro Actions (добавлены в v4.15) — это типобезопасный способ вызывать серверные функции прямо из клиентских компонентов. Никаких ручных fetch и JSON.parse — только чистый TypeScript.
Что такое Astro Actions?
Заголовок раздела «Что такое Astro Actions?»Actions решают задачу: «как клиентский код безопасно общается с сервером?». Вместо того чтобы писать API-маршруты вручную, вы определяете функции в специальном файле — и Astro автоматически создаёт типизированный клиент.
Определение действий
Заголовок раздела «Определение действий»Создайте файл src/actions/index.ts:
import { defineAction, ActionError } from 'astro:actions';import { z } from 'astro:schema';
export const server = { contact: defineAction({ input: z.object({ name: z.string().min(2, 'Имя слишком короткое'), email: z.string().email('Неверный email'), message: z.string().min(10, 'Сообщение слишком короткое'), }), handler: async ({ name, email, message }) => { // Серверный код: отправка email, запись в БД... return { success: true, id: crypto.randomUUID() }; }, }),};Использование в клиентском компоненте
Заголовок раздела «Использование в клиентском компоненте»import { actions } from 'astro:actions';
// Вызов actionconst result = await actions.contact({ name: 'Алекс', message: 'Хочу узнать подробнее...',});
if (result.error) { console.error(result.error.message);} else { console.log('Отправлено! ID:', result.data.id);}Использование в HTML-формах
Заголовок раздела «Использование в HTML-формах»Astro Actions поддерживают нативные HTML-формы без JavaScript:
---import { actions } from 'astro:actions';const result = Astro.getActionResult(actions.contact);---
<form method="POST" action={actions.contact}> <input name="name" type="text" required /> <input name="email" type="email" required /> <textarea name="message" required></textarea> <button type="submit">Отправить</button></form>
{result?.error && <p class="error">{result.error.message}</p>}{result?.data?.success && <p class="success">Отправлено!</p>}Обработка ошибок с ActionError
Заголовок раздела «Обработка ошибок с ActionError»import { defineAction, ActionError } from 'astro:actions';
handler: async ({ email }) => { const user = await db.findUser(email);
if (!user) { throw new ActionError({ code: 'NOT_FOUND', // Стандартные HTTP-коды message: 'Пользователь не найден', }); }
if (!user.isVerified) { throw new ActionError({ code: 'FORBIDDEN', message: 'Email не подтверждён', }); }
return user;}Валидация с Zod
Заголовок раздела «Валидация с Zod»Astro Actions используют Zod для валидации входных данных. Ошибки валидации автоматически возвращаются клиенту:
input: z.object({ age: z.number().min(18, 'Только для взрослых'), username: z.string() .min(3) .max(20) .regex(/^[a-z0-9_]+$/, 'Только a-z, 0-9, _'), tags: z.array(z.string()).max(5, 'Максимум 5 тегов'),}),Оптимистичные обновления UI
Заголовок раздела «Оптимистичные обновления UI»import { actions, isInputError } from 'astro:actions';
const [optimistic, setOptimistic] = useState(null);
const handleSubmit = async (data) => { // Мгновенно показываем результат setOptimistic({ ...data, pending: true });
const result = await actions.createPost(data);
if (result.error) { setOptimistic(null); // Откатываем showError(result.error); } else { setOptimistic({ ...result.data, pending: false }); }};Типобезопасность
Заголовок раздела «Типобезопасность»Astro генерирует TypeScript-типы автоматически. Ваша IDE показывает автодополнение для входных данных и результата:
// Типы генерируются из вашей Zod-схемыtype ContactInput = { name: string; // z.string().min(2) email: string; // z.string().email() message: string; // z.string().min(10)};
type ContactResult = { success: boolean; id: string;};