Svelte

Деплой и окружения

Перед релизом разработчик кладёт ключ платёжного API в переменную и читает её в компоненте через обычный import.meta.env, чтобы быстро проверить интеграцию. Сборка проходит, всё работает. Через день кто-то открывает исходники бандла в браузере и находит там секретный ключ открытым текстом - он попал в клиентский код. SvelteKit предотвращает это на уровне модулей окружения: `$env`/static/private просто не импортируется в код, который едет в браузер, и сборка падает с понятной ошибкой вместо тихой утечки.

  • SvelteKit разделяет переменные окружения на четыре модуля (static/dynamic, public/private) и не даёт приватным попасть в клиентский бандл
  • Vercel, Netlify и Cloudflare исполняют серверный код SvelteKit в своих рантаймах, и выбор адаптера определяет, какие API сервера доступны
  • Edge-рантайм (Cloudflare Workers, Vercel Edge) запускает код в дата-центре рядом с пользователем, но без полного Node API
  • Утечка секрета в клиентский бандл - классический инцидент безопасности; защита окружения на уровне фреймворка снижает этот риск
  • Статический адаптер не может исполнять серверный код в рантайме, поэтому динамические $env-переменные с ним не работают - это ловится при сборке

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

  • Понятие адаптера SvelteKit и пререндеринга: что сборка может быть статической или серверной
  • Базовое различие между кодом, который выполняется на сервере, и кодом, который едет в браузер
  • Идея переменной окружения: внешнее значение, которое не зашито в код (ключ API, URL базы)

Выбор адаптера под задачу

Адаптер задаёт, в какой среде будет жить приложение, и это первое решение деплоя. Оно не косметическое: от него зависит, доступен ли серверный код в рантайме, можно ли читать динамические переменные окружения и какие API платформы есть под рукой. Выбор идёт от характера приложения, а не от моды.

Статический адаптер собирает приложение в набор файлов, которые отдаёт любой CDN или файловый хостинг. Серверного кода в рантайме нет вовсе - всё, что нужно динамически, либо пререндерится, либо уходит в сторонний API с клиента. Это самый дешёвый и устойчивый вариант, но он подходит только когда серверная логика в момент запроса не нужна.

Распространённая ошибка - выбрать adapter-static для приложения с серверными load-функциями, которые должны выполняться на каждый запрос. Сборка либо упадёт, либо отдаст устаревший пререндер. Если данные зависят от запроса (авторизация, персонализация), нужен серверный или edge-адаптер, а не статика.

Приложению нужны серверные load-функции, выполняющиеся на каждый запрос (персонализация по авторизованному пользователю). Какой адаптер не подойдёт?

Edge или node: где исполняется код

Когда выбран серверный рантайм, встаёт следующий выбор: edge или node. Node-рантайм - привычный сервер с полным набором Node API (файловая система, нативные модули, любые npm-пакеты для сервера). Обычно он работает в одном регионе, и пользователь с другого континента платит за дальность сетевой задержкой.

Edge-рантайм (Cloudflare Workers, Vercel Edge) разворачивает код в множестве дата-центров по миру, и запрос обрабатывается в ближайшем к пользователю. Это режет задержку до первого байта. Плата за это - урезанная среда: нет полного Node API, ограничено время выполнения и доступная память, часть npm-пакетов, рассчитанных на Node, там не запустится.

  • Node-рантайм — Полный Node API, любые серверные пакеты, длительные операции. Один регион, поэтому выше задержка для удалённых пользователей. Подходит для тяжёлой серверной логики и работы с БД через нативные драйверы.
  • Edge-рантайм — Запуск рядом с пользователем, низкая задержка, хорошая глобальная масштабируемость. Урезанный рантайм без полного Node API, лимиты на время и память. Подходит для лёгкой логики: проверка авторизации, редиректы, персонализация заголовков.
КритерийNodeEdge
Задержка для глобальных пользователейВыше, зависит от регионаНизкая, исполнение рядом
Полный Node APIДаНет, урезанный набор
Тяжёлые и длительные операцииПодходитОграничено лимитами
Совместимость npm-пакетовШирокаяТолько web-совместимые

Здравая стратегия - не выбирать edge ради хайпа. Edge выигрывает на лёгкой логике, чувствительной к задержке: гео-редиректы, A/B-распределение, проверка токена. Тяжёлая обработка, работа с нативными драйверами БД и длительные задачи лучше живут на node. Многие приложения комбинируют: статика и лёгкая логика на edge, тяжёлое на node.

Почему нельзя считать edge-рантайм просто 'быстрым node' и переносить туда любой серверный код?

Модули окружения: static, dynamic, public, private

SvelteKit не даёт читать переменные окружения как попало. Их делят по двум осям. Первая: static против dynamic. Статическая переменная вшивается в код при сборке, и сборщик может удалить мёртвые ветки по ней. Динамическая читается в рантайме на сервере, поэтому её можно менять без пересборки, но статический адаптер её не поддерживает. Вторая ось: public против private. Публичная может попасть в клиент, приватная - только на сервер.

МодульКогда читаетсяГде доступен
$env/static/privateВшивается при сборкеТолько сервер
$env/static/publicВшивается при сборкеСервер и клиент (префикс PUBLIC_)
$env/dynamic/privateЧитается в рантайме сервераТолько сервер, не статический адаптер
$env/dynamic/publicЧитается в рантаймеСервер и клиент (префикс PUBLIC_)

Импорт `$env`/static/private в код, который выполняется в браузере (компонент, универсальный load), ломает сборку с явной ошибкой. Это и есть защита: фреймворк не позволяет молча запечь секрет в клиентский бандл. Публичные переменные обязаны иметь префикс PUBLIC_ именно чтобы было видно по имени, что значение не секретно.

Где безопасно прочитать секретный ключ платёжного API в SvelteKit?

Что попадает в сборку

Итоговая сборка SvelteKit это не один файл, а два мира: клиентские ассеты, которые поедут в браузер, и серверная часть, которую исполнит платформа (если адаптер не статический). Понимание границы между ними - ключ и к безопасности, и к размеру. Всё, что импортируется в клиентский граф, едет к пользователю; всё, что только в серверном, остаётся на сервере.

  • Клиентские ассеты: JS-чанки компонентов, CSS, статические файлы - всё видно в браузере
  • Серверный модуль: load на сервере, эндпоинты, hooks - не попадает в клиент
  • Приватные $env: остаются на сервере, в клиентский бандл их импортировать нельзя
  • Файлы .server.ts: код, который компилятор не пустит в клиентский граф по построению

Суффикс .server в имени файла (+page.server.ts, lib/db.server.ts) - явный сигнал и компилятору, и человеку: этот модуль только серверный. SvelteKit не даст импортировать его в клиентский код. Это вторая линия защиты после модулей `$env`: даже если в серверном модуле есть прямое чтение секрета через process.env, сам модуль в браузер не попадёт.

Полезная привычка перед первым продакшен-деплоем: собрать локально и поискать в клиентском бандле имена секретов. Если grep по клиентскому выводу ничего не находит, граница соблюдена. Это дешёвая проверка, которая ловит случайную утечку до того, как её найдёт посторонний.

Зачем нужен суффикс .server в имени модуля (например db.server.ts)?

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

Деплой опирается на адаптеры и смыкается с безопасностью. Связи:

  • Экосистема и адаптеры — Адаптеры как механизм впервые вводятся в уроке об экосистеме; здесь разбирается выбор между ними и его последствия
  • Доступность — Аналогия по духу: ранняя проверка фреймворком предотвращает класс проблем до прода - там a11y, тут утечка секретов

Итог

  • Выбор адаптера определяет среду выполнения: статика без сервера, Node-сервер, функции хостинга или edge-воркер
  • Edge запускает код близко к пользователю с низкой задержкой, но в урезанном рантайме без полного Node API; node даёт полный доступ, но из одного региона
  • $env делится по двум осям: static (значение вшито при сборке) и dynamic (читается в рантайме), public (можно в клиент) и private (только сервер)
  • Приватный модуль $env нельзя импортировать в код, идущий в браузер: попытка ломает сборку, а не молча утекает секрет в бандл
  • Публичные переменные обязаны иметь префикс PUBLIC_ и видны в клиенте, поэтому в них кладут только не секретные значения вроде публичного URL

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

  • sv-41-svelte-ecosystem — Адаптеры впервые разбираются в уроке об экосистеме; здесь они применяются к выбору платформы и формата сборки
  • sv-39-accessibility-a11y-warnings — Как компилятор ловит a11y-проблемы до прода, так модули `$env` ловят утечку секретов в клиент до деплоя
Деплой и окружения

0

1

Войти