Перейти к содержимому

33. Astro Actions

Astro Actions (добавлены в v4.15) — это типобезопасный способ вызывать серверные функции прямо из клиентских компонентов. Никаких ручных fetch и JSON.parse — только чистый TypeScript.

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, запись в БД...
await sendEmail({ to: '[email protected]', name, email, message });
return { success: true, id: crypto.randomUUID() };
},
}),
};
import { actions } from 'astro:actions';
// Вызов action
const result = await actions.contact({
name: 'Алекс',
message: 'Хочу узнать подробнее...',
});
if (result.error) {
console.error(result.error.message);
} else {
console.log('Отправлено! ID:', result.data.id);
}

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

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 тегов'),
}),
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;
};