Server Actions и формы
Безопасные мутации, useActionState, валидация и оптимистичный UI.
Обновлено
Что такое Server Action
Server Action — async-функция с пометкой 'use server', которую вы вызываете из интерфейса (форма, кнопка), но которая выполняется на сервере. Next автоматически создаёт эндпоинт и делает запрос за вас.
Их используют для мутаций: создание, редактирование, удаление, сохранение. Они заменяют связку «API-маршрут + fetch + состояние загрузки» для ваших собственных форм.
// features/create-post/api/create-post.ts
'use server'
import { updateTag } from 'next/cache'
import { redirect } from 'next/navigation'
export async function createPost(prev: State, formData: FormData) {
const session = await auth()
if (!session) return { error: 'Нет доступа' }
const title = String(formData.get('title') ?? '').trim()
if (title.length < 3) return { error: 'Слишком короткий заголовок' }
const post = await db.post.create({ data: { title, authorId: session.userId } })
updateTag('posts')
redirect(`/posts/${post.id}`)
}Это приложение сохраняет ваш прогресс ровно так (entities/progress/api/save-progress.ts).
Безопасность — самое важное
Server Action — это публичный POST-эндпоинт. Любой может вызвать его напрямую, без вашего интерфейса, с любыми данными.
- Проверяйте аутентификацию и авторизацию в каждом действии («вошёл ли?» и «есть ли доступ к этому ресурсу?»).
- Валидируйте весь ввод (zod / valibot) — в
formDataможет быть что угодно. - Не возвращайте чувствительные данные.
Способы вызвать Server Action
| Способ | Код | Когда |
|---|---|---|
<form action={fn}> |
получает FormData |
по умолчанию для форм; работает даже без JS |
useActionState(fn, initial) |
получаете [state, action, pending] |
показать ошибки / сообщения / pending |
<button formAction={fn}> |
несколько действий в одной форме | «Сохранить» и «Удалить» |
| прямой вызов в обработчике | await fn(args) |
взаимодействия, не являющиеся формами (лайк, автосохранение) |
Форма с useActionState
'use client'
import { useActionState } from 'react'
export function PostForm() {
const [state, action, pending] = useActionState(createPost, { error: null })
return (
<form action={action}>
<input name="title" required />
<p aria-live="polite">{state.error}</p>
<button disabled={pending}>{pending ? 'Сохранение…' : 'Сохранить'}</button>
</form>
)
}С useActionState действие получает предыдущее состояние первым аргументом, а FormData — вторым.
Progressive enhancement: форма, отрендеренная на сервере с Server Action, работает, даже если JS ещё не загрузился.
Полный сценарий с валидацией Zod и сохранением значений после ошибки — в Формы с Server Actions.
Оптимистичный UI
const [optimisticLikes, addLike] = useOptimistic(likes, (state, n: number) => state + n)
<form action={async () => { addLike(1); await like(postId) }}>Интерфейс меняется мгновенно; если действие не удалось, React вернёт реальное значение.
Server Action или Route Handler
| Server Action | Route Handler |
|---|---|
| формы и кнопки вашего приложения | API для других (мобильные, партнёры) |
| мутации | вебхуки (Stripe, CMS) |
| вызываются последовательно, по одному | ответы не в HTML (RSS, файлы, публичный JSON) |
Чтение данных через Server Actions не делают — для этого есть Server Components.
Коротко
- Server Action = функция
'use server', вызываемая из UI и выполняемая на сервере; для мутаций. - В каждом действии: аутентификация + авторизация + валидация.
<form action>+useActionStateдля ошибок и pending;updateTagпосле мутации.