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

Серверы Node

Для создания автономного сервера Node используйте adapter-node.

Установите адаптер с помощью команды npm i -D @sveltejs/adapter-node, затем добавьте его в ваш файл svelte.config.js:

svelte.config.js
import adapter from '@sveltejs/adapter-node';
export default {
kit: {
adapter: adapter()
}
};

Сначала соберите приложение с помощью команды npm run build. Это создаст продакшен-сервер в выходной директории, указанной в настройках адаптера, по умолчанию — build.

Для запуска приложения вам понадобятся выходная директория, файл package.json проекта и производственные зависимости в node_modules. Производственные зависимости можно установить, скопировав файлы package.json и package-lock.json, а затем выполнив команду npm ci --omit dev (этот шаг можно пропустить, если у вашего приложения нет зависимостей). После этого вы можете запустить приложение с помощью следующей команды:

Окно терминала
node build

Зависимости для разработки будут включены в ваше приложение с использованием Rollup. Чтобы определить, будет ли определённый пакет включён в сборку или останется внешним, указывайте его в devDependencies или dependencies соответственно в вашем файле package.json.

Обычно требуется сжимать ответы, отправляемые с сервера. Если вы уже разворачиваете сервер за обратным прокси для SSL или балансировки нагрузки, сжатие лучше выполнять на этом уровне, так как это обычно обеспечивает лучшую производительность, учитывая, что Node.js является однопоточным.

Однако, если вы создаёте пользовательский сервер и хотите добавить мидлвар для сжатия, мы рекомендуем использовать @polka/compression, поскольку SvelteKit использует потоковую передачу ответов, а более популярный пакет compression не поддерживает стриминг и может вызывать ошибки при использовании.

В режимах dev и preview SvelteKit читает переменные окружения из файла .env (или .env.local, или .env.[mode], как определено в Vite).

В продакшене файлы .env не загружаются автоматически. Чтобы это сделать, установите dotenv в ваш проект…

Окно терминала
npm install dotenv

…и вызовите его перед запуском собранного приложения:

Окно терминала
node -r dotenv/config build

Если вы используете Node.js версии 20.6 или выше, вы можете вместо этого использовать флаг --env-file:

Окно терминала
node --env-file=.env build

По умолчанию сервер принимает соединения на 0.0.0.0 через порт 3000. Эти параметры можно настроить с помощью переменных окружения PORT и HOST:

Окно терминала
HOST=127.0.0.1 PORT=4000 node build

В качестве альтернативы сервер можно настроить для приема соединений по указанному пути сокета. Если это делается с помощью переменной окружения SOCKET_PATH, переменные окружения HOST и PORT будут игнорироваться.

Окно терминала
SOCKET_PATH=/tmp/socket node build

HTTP не предоставляет SvelteKit надежного способа узнать URL, который запрашивается в данный момент. Самый простой способ указать SvelteKit, где размещено приложение, — это установить переменную окружения ORIGIN:

Окно терминала
ORIGIN=https://my.site node build
# или, например, для локального предпросмотра и тестирования
ORIGIN=http://localhost:3000 node build

С помощью этого запрос к пути /stuff будет корректно разрешаться в https://my.site/stuff. В качестве альтернативы вы можете указать заголовки, которые сообщат SvelteKit о протоколе запроса и хосте, на основе которых он сможет сформировать исходный URL:

Окно терминала
PROTOCOL_HEADER=x-forwarded-proto HOST_HEADER=x-forwarded-host node build

Если adapter-node не может корректно определить URL вашего развёртывания, вы можете столкнуться с ошибкой при использовании действий форм:

Объект RequestEvent, передаваемый в хуки и конечные точки, включает функцию event.getClientAddress(), которая возвращает IP-адрес клиента. По умолчанию это адрес подключения remoteAddress. Если ваш сервер находится за одним или несколькими прокси (например, балансировщиком нагрузки), это значение будет содержать IP-адрес самого внутреннего прокси, а не клиента, поэтому необходимо указать ADDRESS_HEADER для чтения адреса:

Окно терминала
ADDRESS_HEADER=True-Client-IP node build

Если ADDRESS_HEADER установлен как X-Forwarded-For, значение заголовка будет содержать список IP-адресов, разделённых запятыми. Переменная окружения XFF_DEPTH должна указывать, сколько доверенных прокси находится перед вашим сервером. Например, если есть три доверенных прокси, прокси 3 будет пересылать адреса исходного соединения и первых двух прокси:

<client address>, <proxy 1 address>, <proxy 2 address>

Некоторые руководства советуют читать самый левый адрес, но это делает вас уязвимым для подделки:

<spoofed address>, <client address>, <proxy 1 address>, <proxy 2 address>

Вместо этого мы читаем с правой стороны, учитывая количество доверенных прокси. В данном случае мы используем XFF_DEPTH=3.

Максимальный размер тела запроса в байтах, принимаемый при потоковой передаче. Размер тела можно также указать с суффиксом единицы измерения для килобайт (K), мегабайт (M) или гигабайт (G). Например, 512K или 1M. По умолчанию — 512 КБ. Эту опцию можно отключить, установив значение Infinity (в старых версиях адаптера — 0) и реализовав пользовательскую проверку в handle, если требуется более сложная логика.

Количество секунд ожидания перед принудительным закрытием оставшихся соединений после получения сигнала SIGTERM или SIGINT. По умолчанию — 30. Внутри адаптер вызывает closeAllConnections. Подробности см. в разделе Грамотное завершение работы.

При использовании активации сокетов systemd переменная IDLE_TIMEOUT указывает количество секунд, после которых приложение автоматически переводится в спящий режим, если запросы не поступают. Если не установлено, приложение работает непрерывно. Подробности см. в разделе Активация сокетов.

Адаптер можно настроить с помощью различных параметров:

svelte.config.js
import adapter from '@sveltejs/adapter-node';
export default {
kit: {
adapter: adapter({
// параметры по умолчанию
out: 'build',
precompress: true,
envPrefix: ''
})
}
};

Директория, в которую собирается сервер. По умолчанию — build, то есть команда node build запустит сервер локально после его создания.

Включает предварительное сжатие с использованием gzip и brotli для активов и предварительно отрендеренных страниц. По умолчанию — true.

Если вам нужно изменить названия переменных окружения, используемых для настройки развёртывания (например, чтобы избежать конфликтов с переменными окружения, которые вы не контролируете), вы можете указать префикс:

envPrefix: 'MY_CUSTOM_';
Окно терминала
MY_CUSTOM_HOST=127.0.0.1 \
MY_CUSTOM_PORT=4000 \
MY_CUSTOM_ORIGIN=https://my.site \
node build

По умолчанию adapter-node выполняет корректное завершение работы HTTP-сервера при получении сигнала SIGTERM или SIGINT. Процесс включает:

  1. Отклонение новых запросов server.close.
  2. Ожидание завершения уже поступивших, но ещё не обработанных запросов, и закрытие соединений, как только они станут неактивными server.closeIdleConnections.
  3. Принудительное закрытие всех оставшихся активных соединений после истечения времени, указанного в SHUTDOWN_TIMEOUT (в секундах) server.closeAllConnections.

Вы можете подписаться на событие sveltekit:shutdown, которое вызывается после того, как HTTP-сервер закроет все соединения. В отличие от события exit в Node, событие sveltekit:shutdown поддерживает асинхронные операции и всегда вызывается после закрытия всех соединений, даже если у сервера остались незавершённые задачи, такие как открытые подключения к базе данных.

process.on('sveltekit:shutdown', async (reason) => {
await jobs.stop();
await db.close();
});

Параметр reason принимает одно из следующих значений:

  • SIGINT — завершение работы инициировано сигналом SIGINT
  • SIGTERM — завершение работы инициировано сигналом SIGTERM
  • IDLE — завершение работы инициировано по истечении времени IDLE_TIMEOUT

Большинство современных операционных систем Linux используют менеджер процессов systemd для запуска сервера и управления сервисами. Вы можете настроить сервер для выделения сокета и запуска или масштабирования приложения по требованию. Это называется активацией сокетов. В этом случае операционная система передаст приложению две переменные окружения — LISTEN_PID и LISTEN_FDS. Адаптер будет прослушивать файловый дескриптор 3, который соответствует юниту сокета systemd, который вам нужно будет создать.

Чтобы воспользоваться активацией сокетов, выполните следующие шаги:

  1. Запустите приложение как сервис systemd. Оно может работать непосредственно на хост-системе или внутри контейнера (например, с использованием Docker или портативного сервиса systemd). Если вы дополнительно передадите приложению переменную окружения IDLE_TIMEOUT, оно будет грамотно завершать работу, если в течение указанного времени не поступает запросов. systemd автоматически перезапустит приложение, когда поступят новые запросы.

    /etc/systemd/system/myapp.service
    [Service]
    Environment=NODE_ENV=production IDLE_TIMEOUT=60
    ExecStart=/usr/bin/node /usr/bin/myapp/build
  2. Создайте соответствующий юнит сокета. Адаптер принимает только один сокет.

    /etc/systemd/system/myapp.socket
    [Socket]
    ListenStream=3000
    [Install]
    WantedBy=sockets.target
  3. Убедитесь, что systemd распознал оба юнита, выполнив команду sudo systemctl daemon-reload. Затем включите запуск сокета при загрузке системы и запустите его немедленно с помощью команды sudo systemctl enable --now myapp.socket. После этого приложение автоматически запустится при первом запросе к localhost:3000.

Адаптер создаёт два файла в вашей директории сборки — index.js и handler.js. Запуск index.js — например, node build, если используется директория сборки по умолчанию — запустит сервер на настроенном порту.

В качестве альтернативы вы можете импортировать файл handler.js, который экспортирует обработчик, подходящий для использования с Express, Connect или Polka (или даже просто со встроенным http.createServer), и настроить собственный сервер:

my-server.js
import { handler } from './build/handler.js';
import express from 'express';
const app = express();
// добавляем маршрут, который работает отдельно от приложения SvelteKit
app.get('/healthcheck', (req, res) => {
res.end('ok');
});
// пусть SvelteKit занимается всем остальным, включая обслуживание предварительно отрисованных страниц и статических ресурсов
app.use(handler);
app.listen(3000, () => {
console.log('listening on port 3000');
});