React

Server Functions и 'use server'

Чтобы сохранить форму, фронтендер годами писал один и тот же ритуал: придумать URL эндпоинта, объявить роут на бэкенде, на клиенте собрать fetch с методом, заголовками и JSON.stringify, на сервере распарсить тело, на клиенте обработать ответ. Половина кода - транспорт. Server Functions убирают этот слой. Помечаешь async-функцию строкой 'use server', импортируешь и вызываешь её прямо из клиентского компонента как обычную функцию. React сам превращает вызов в сетевой запрос к серверу.

  • React 19 (декабрь 2024): Server Functions (изначально Server Actions) входят в стабильный API
  • Next.js App Router: 'use server' - дефолтный способ делать мутации (создать заказ, обновить профиль) без ручных API-роутов
  • Vercel: документация и шаблоны строят формы и мутации поверх Server Functions с валидацией на сервере
  • React Docs: официальная документация описывает 'use server' как директиву границы клиент-сервер для вызовов
  • Remix и React Router: похожая идея серверных action'ов давно использовалась и теперь сходится с моделью React

Предварительные знания

  • Server Components и граница сериализации, директива 'use client'
  • Понимание мутации данных: создание, обновление, удаление записей на сервере
  • Базовые представления о безопасности: сессии, аутентификация, валидация входных данных

Как RPC вернулся во фронтенд под именем Server Functions

Идея вызывать удалённую функцию так, будто она локальная, стара как сети: это RPC, remote procedure call. React переоткрыл её для фронтенда. Сначала фича называлась Server Actions и была заточена под формы. К стабильному релизу React 19 её обобщили и переименовали в Server Functions: это любая async-функция с директивой 'use server', которую клиент может вызвать, а выполнится она на сервере. React и фреймворк под капотом генерируют для каждой такой функции защищённый эндпоинт, сериализуют аргументы, делают POST-запрос и десериализуют результат. Разработчик пишет просто await createPost(data), не думая о транспорте.

Что такое Server Function

Server Function - это async-функция, помеченная директивой 'use server'. Её можно импортировать в клиентский компонент и вызвать как обычную функцию, но выполнится она на сервере. React под капотом превращает вызов в сетевой запрос: сериализует аргументы, отправляет их на сгенерированный эндпоинт, выполняет тело на сервере и возвращает сериализованный результат. Для разработчика это выглядит как локальный вызов, по сути - управляемый RPC.

Директиву можно ставить двумя способами: в начале файла - тогда серверными становятся все экспортируемые функции модуля; или внутри тела отдельной функции - тогда серверной становится только она. Аргументы и возвращаемое значение проходят ту же границу сериализации, что и props в RSC: передавать можно сериализуемые данные, но не, например, DOM-узлы.

Между Server Function и Server Component есть разница. Server Component рендерит UI на сервере. Server Function не рендерит ничего - это вызываемая операция, обычно мутация. Объединяет их одно: оба выполняются на сервере и имеют доступ к базе и секретам, недоступным клиенту.

Что происходит, когда клиентский компонент вызывает функцию, помеченную 'use server'?

Мутации и обновление данных

Главное назначение Server Functions - мутации: операции, меняющие состояние на сервере. Создать заказ, обновить профиль, удалить комментарий, поставить лайк. После мутации почти всегда нужно показать свежие данные. Здесь Server Function работает в паре с механизмом инвалидации: фреймворк перерендеривает затронутые серверные компоненты или сбрасывает кэш, и клиент видит обновлённый UI без ручного перезапроса.

Поток мутации выглядит так: клиент вызывает Server Function, та меняет данные на сервере и помечает затронутые маршруты или кэш-теги как устаревшие. Фреймворк перерендеривает нужные серверные компоненты и стримит обновление обратно. Разработчику не нужно вручную дёргать API заново и руками класть свежий ответ в состояние - инвалидация делает это за него.

Server Function может возвращать значение клиенту: id созданной записи, объект с результатом или с ошибкой. Это пригодится в следующем уроке, где useActionState читает возвращённое значение как состояние формы. Возвращать ошибку как данные обычно удобнее, чем бросать исключение.

Зачем после мутации в Server Function вызывают инвалидацию (например, revalidatePath)?

Безопасность: проверять сессию и вход всегда

Главное, что нужно усвоить про Server Functions: каждая из них - это публичный эндпоинт. React генерирует под неё URL, и его может вызвать кто угодно с подделанными аргументами, в обход формы и интерфейса. Поэтому удобство 'вызвал функцию как локальную' не отменяет ни одного правила серверной безопасности. Внутри Server Function обязательно проверять, кто вызывает (сессия и права) и что именно пришло (валидация входных данных).

Порядок проверок важен. Сначала аутентификация и авторизация: есть ли сессия, имеет ли пользователь право на это действие. Затем валидация: разобрать вход по строгой схеме (например, zod) и отвергнуть всё, что не подходит. Только после двух зелёных проверок выполнять саму мутацию. Аргументы приходят из сети, и доверять им нельзя - это базовый принцип любого серверного API.

Server Function не защищена от подделки запроса автоматически: вызов может прийти не из вашей формы, а от стороннего скрипта. Фреймворк добавляет базовую защиту от CSRF для action-вызовов, но проверку прав и валидацию входа это не заменяет. Никогда не вычисляйте, например, цену или права на основе данных, присланных клиентом, без перепроверки на сервере.

Почему внутри Server Function обязательно проверять сессию и валидировать аргументы?

Связь с другими темами

Server Functions - серверная сторона мутаций. Что рядом:

  • Actions и useActionState — Server Function обычно подаётся в action формы, а useActionState ведёт её состояние и ошибки
  • Server Components — То же серверное окружение и та же граница сериализации для аргументов и результата
  • Next.js App Router — Фреймворк генерирует эндпоинт под каждую Server Function и связывает её с роутингом и кэшем

Итог

  • Server Function - это async-функция с директивой 'use server'; вызывается из клиента, но тело выполняется на сервере как управляемый RPC
  • React и фреймворк сами генерируют эндпоинт, сериализуют аргументы и результат - ручной fetch, URL и парсинг тела больше не нужны
  • Главный сценарий - мутации: создать, обновить, удалить запись, после чего обновить кэш или перерендерить затронутые данные
  • Server Function - это публичная точка входа в сервер: внутри ОБЯЗАТЕЛЬНО проверять сессию (авторизация) и валидировать все входные данные
  • Аргументы приходят от клиента и им нельзя доверять: проверка прав и схемы данных защищает от подделки запросов и инъекций

Связанные уроки

  • rc-32-actions-useactionstate — Server Function чаще всего вызывается как action формы, а useActionState управляет её состоянием на клиенте
  • rc-30-rsc-intro — Server Functions работают в том же серверном окружении, что и Server Components, и полагаются на ту же границу
  • rc-35-nextjs-app-router — В App Router Server Functions - штатный способ выполнять мутации без ручного написания API-роутов
Server Functions и 'use server'

0

1

Войти