Svelte
Hooks: handle и серверная логика
Каждая защищённая страница и каждый API-эндпоинт должны знать, кто сейчас вошёл в систему. Дублировать разбор cookie с токеном в каждом load и каждом +server.js - путь к багам и забытым проверкам. SvelteKit даёт одну точку, через которую проходит абсолютно каждый запрос к серверу: функцию handle в hooks.server.js. Здесь один раз читается сессия, в event.locals кладётся пользователь, и дальше любой load, action и эндпоинт берут готовое locals.user, не зная деталей аутентификации.
- Аутентификация: handle читает cookie сессии, кладёт пользователя в locals, все маршруты пользуются им единообразно
- Защита разделов: один handle проверяет, что путь /admin доступен только роли admin, и редиректит остальных
- Логирование и метрики: handle замеряет длительность каждого запроса и пишет в лог метод, путь и статус
- Локализация: handle определяет язык из cookie или заголовка и подставляет его в lang атрибут HTML
- Проксирование API: handleFetch переписывает внутренние fetch на прямой вызов бэкенда, минуя публичный домен
Предварительные знания
- Жизненный цикл запроса SvelteKit и роли load, actions, +server.js
- HTTP cookie, заголовки и понятие сессии
- Понятие middleware: код, который оборачивает обработку запроса
handle: одна точка для каждого запроса
Файл src/hooks.server.js экспортирует функцию handle - серверный middleware SvelteKit. Через неё проходит каждый запрос: к странице, к form action, к +server.js эндпоинту. handle получает объект с event (текущий запрос) и resolve (функция, которая запускает обычную маршрутизацию и возвращает Response). Обязанность handle - вызвать resolve(event) и вернуть его результат, опционально что-то сделав до или после.
Код до resolve выполняется перед маршрутизацией - тут читают cookie, проверяют доступ. Код после resolve видит готовый Response и может его дополнить, например выставить заголовок. handle обязан вернуть Response: либо результат resolve, либо собственный ответ, если запрос нужно прервать раньше.
Если handle не определён, SvelteKit использует поведение по умолчанию, эквивалентное async ({ event, resolve }) => resolve(event). То есть запрос просто проходит маршрутизацию без дополнительной логики.
Какова основная обязанность функции handle в hooks.server.js?
event.locals: авторизация и сессия
event.locals - это объект для данных, которые handle хочет передать дальше по обработке текущего запроса. Он живёт ровно один запрос и доступен во всех server load, actions и +server.js этого запроса. Типичный сценарий: handle читает cookie сессии, находит пользователя и кладёт его в event.locals.user. После этого ни один load не разбирает токен сам.
Нельзя хранить пользовательские данные в модульной переменной на верхнем уровне hooks.server.js. Серверный модуль один на все запросы и параллельных пользователей, поэтому такая переменная утечёт между ними. Данные конкретного запроса кладут только в event.locals - этот объект свой у каждого запроса.
Объект event.cookies даёт безопасный доступ к cookie текущего запроса: get для чтения, set и delete для записи с автоматической установкой пути и флагов безопасности. Это предпочтительнее ручного разбора заголовка Cookie. Редирект через redirect() прерывает обработку до resolve - удобно для защиты разделов.
Куда handle должен положить вошедшего пользователя, чтобы любой server load и action этого запроса могли его прочитать?
handleFetch и композиция через sequence
Кроме handle, hooks.server.js может экспортировать handleFetch. Этот хук перехватывает каждый вызов fetch, сделанный из load или эндпоинта через переданный event.fetch. Он позволяет переписать запрос: подменить публичный URL на внутренний адрес сервиса, добавить заголовок авторизации, перенаправить трафик в обход интернета внутри одной сети.
Когда серверной логики становится много - аутентификация, локализация, логирование - держать всё в одной функции handle неудобно. SvelteKit даёт хелпер sequence из @sveltejs/kit/hooks: он принимает несколько функций handle и объединяет их в одну цепочку. Хуки выполняются слева направо до resolve и в обратном порядке после.
Каждый handle в sequence отвечает за одну заботу: один за сессию, другой за язык, третий за логи. Это держит хуки маленькими и читаемыми, а порядок выполнения остаётся явным - тем, в котором они переданы в sequence.
В приложении нужны три независимые серверные заботы: аутентификация, локализация и логирование. Как организовать их в hooks.server.js?
Связь с другими темами
Hooks - серверный слой перед всеми обработчиками. Он питает данными остальные части маршрута:
- Load-функции — Server load читает locals.user, который наполнил handle, вместо ручного разбора токена
- Form actions — Action проверяет права через locals, подготовленные handle до запуска обработчика формы
- Состояние в SvelteKit — locals - корректный request-scoped контейнер; урок о состоянии объясняет опасность общей серверной переменной
Итог
- handle в hooks.server.js перехватывает каждый серверный запрос; внутри вызывается resolve(event) для продолжения обработки
- event.locals - request-scoped объект для своих данных (user, session); он живёт ровно один запрос и виден в load, actions, +server.js
- handle может ответить сам (редирект, 403) до resolve или модифицировать ответ после resolve
- handleFetch перехватывает fetch из load и эндпоинтов, позволяя переписывать URL и заголовки внутренних запросов
- sequence из @sveltejs/kit/hooks объединяет несколько handle в цепочку, выполняемую по порядку
Связанные уроки
- sv-19-load-functions — handle наполняет event.locals.user, и server load берёт оттуда пользователя без повторного разбора cookie
- sv-20-form-actions — Action проверяет авторизацию через locals, которые подготовил handle до его вызова
- sv-24-state-management-kit — locals - официальный request-scoped контейнер на сервере; урок о состоянии объясняет, почему именно так, а не глобальная переменная