Pour générer un serveur Node autonome, utilisez [`adapter-node`](https://github.com/sveltejs/kit/tree/main/packages/adapter-node). ## Usage Installer le paquet avec `npm i -D @sveltejs/adapter-node`, puis ajoutez l'adaptateur à votre fichier `svelte/config.js` : ```js // @errors: 2307 /// file: svelte.config.js import adapter from '@sveltejs/adapter-node'; /** @type {import('@sveltejs/kit').Config} */ const config = { kit: { adapter: adapter() } }; export default config; ``` ## Déployer D'abord, compilez votre application avec `npm run build`. Ceci va créer le serveur de production dans le dossier d'output défini dans les options de l'adaptateur, dont l'emplacement par défaut est `build`. Vous aurez besoin du dossier d'output, du fichier `package.json` de votre projet, et des dépendances de production dans votre dossier `node_modules` pour exécuter votre application. Les dépendances de production peuvent être générées en copiant le fichier `package.json` et `package-lock.json`, puis en exécutant `npm ci --omit dev` (vous pouvez passer cette étape si votre application n'a aucune dépendance). Vous pouvez alors démarrer votre application avec la commande suivante : ```sh node build ``` Les dépendances de développement seront empaquetées dans votre application avec [Rollup](https://rollupjs.org). Pour contrôler si un paquet donné est inclus ou externalisé, placez-le dans `devDependencies` ou `dependencies` respectivement, au sein de votre `package.json`. ### Compresser les réponses Vous souhaitez normalement compresser les réponses venant du serveur. Si vous déployez déjà votre serveur derrière un proxy SSL ou un gestionnaire de charge (_load balancer_), il est généralement plus efficace en termes de performances de gérer la compression à ce niveau-là, car Node.js ne peut utiliser qu'un seul thread. Cependant, si vous compilez un [serveur personnalisé](#Custom-server) et ne souhaitez pas y ajouter un middleware de compression, notez que nous vous recommanderions d'utiliser [`@polka/compression`](https://www.npmjs.com/package/@polka/compression) puisque SvelteKit streames ses réponses, et que le paquet `compression`, certes plus populaire, ne supporte pas le streaming, et pourrait donc provoquer des erreurs lorsqu'utilisé avec SvelteKit. ## Variables d'environnement [!VO]Environment variables En modes `dev` et `preview`, SvelteKit va lire les variables d'environnement dans votre fichier `.env` (ou `.env.local` ou `.env.[mode]`, [tel que déterminé par Vite](https://vitejs.dev/guide/env-and-mode.html#env-files).) En production, les fichiers `.env` ne sont _pas_ automatiquement chargés. Pour que ce soit le cas, installez `dotenv` dans votre projet... ```sh npm install dotenv ``` ... et invoquez le avant de lancer votre application compilée : ```sh node +++-r dotenv/config+++ build ``` Si vous utilisez Node.js v20.6+, vous pouvez utiliser l'option [`--env-file`](https://nodejs.org/en/learn/command-line/how-to-read-environment-variables-from-nodejs) à la place : ```sh node +++--env-file=.env+++ build ``` ### `PORT`, `HOST` et `SOCKET_PATH` Par défaut, le serveur va accepter des connections entrantes sur `0.0.0.0` en utilisant le port 3000. Ces valeurs peuvent être personnalisées avec les variables d'environnement `PORT` et `HOST` : ```sh HOST=127.0.0.1 PORT=4000 node build ``` Comme alternative, le serveur peut être configuré pour accepter les connections entrantes sur un chemin de socket défini. Lorsque vous faites en utilisant la variable d'environnement `SOCKET_PATH`, les variables d'environnement `HOST` et `PORT` seront ignorées. ```sh SOCKET_PATH=/tmp/socket node build ``` ### `ORIGIN`, `PROTOCOL_HEADER`, `HOST_HEADER`, and `PORT_HEADER` HTTP ne fournit pas à SvelteKit une manière fiable de connaître l'URL qui est en train d'être requêtée. La manière la plus simple de dire à SvelteKit depuis où l'application est servie est de définir la variable d'environnement `ORIGIN` : ```sh ORIGIN=https://my.site node build # ou par ex. pour prévisualiser et tester ORIGIN=http://localhost:3000 node build ``` Avec ceci, une requête pour le chemin `/stuff` sera correctement résolue en `https://my.site/stuff`. Alternativement, vous pouvez définir les en-têtes précisant à SvelteKit les protocole et hôte de requête, à partir desquels il va pouvoir construire l'URL d'origine : ```sh PROTOCOL_HEADER=x-forwarded-proto HOST_HEADER=x-forwarded-host node build ``` > [!NOTE] > [`x-forwarded-proto`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-Proto) > et > [`x-forwarded-host`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-Host) > sont des standards de facto qui relaient les protocole et hôte originaux si vous utilisez un > reverse proxy (comme les load balancers et CDNs). Vous devriez uniquement définir ces variables si > votre serveur est derrière un reverse proxy de confiance ; sinon, il peut être possible pour des > clients de manipuler ces en–têtes (_spoofing_). > > Si vous hébergez votre proxy sur un port non standard et que votre reverse proxy supporte > l'en-tête `x-forwarded-port`, vous pouvez aussi définir `PORT_HEADER=x-forwarded-port`. Si l'adaptateur `adapter-node` ne peut pas définir correctement l'URL de votre déploiement, il se peut que vous subissiez cette erreur lors de l'utilisation d'[actions de formulaire](form-actions) : > [!NOTE] Cross-site POST form submissions are forbidden (Les soumissions de formulaire cross-site > POST sont interdites). ### `ADDRESS_HEADER` et `XFF_DEPTH` L'objet [`RequestEvent`](@sveltejs-kit#RequestEvent) passé aux hooks et aux endpoints inclut une fonction `event.getClientAddress()` qui renvoie l'adresse IP du client. Par défaut il s'agit de l'adresse de connexion `remoteAddress`. Si votre serveur est derrière un ou plusieurs proxies (comme un load balancer), cette valeur contiendra l'adresse IP du proxy le plus profond et non celle du client, et il est donc nécessaire de spécifier `ADDRESS_HEADER` pour lire l'adresse : ```sh ADDRESS_HEADER=True-Client-IP node build ``` > [!NOTE] Les en-têtes peuvent être facilement manipulées. Comme `PROTOCOL_HEADER` et `HOST_HEADER`, > vous devriez [savoir ce que vous faites](https://adam-p.ca/blog/2022/03/x-forwarded-for/) avant de > les définir. Si la valeur de `ADDRESS_HEADER` est `X-Forwarded-For`, l'en-tête contiendra une liste d'adresses IP séparées par des virgules. La variable d'environnement `X-Forwarded-For` devrait spécifier combien de proxys de confiance se trouvent devant votre serveur. Par ex. s'il y a trois proxys de confiance, le proxy n°3 va relayer les adresses de la connection d'origine et des 2 premiers proxys : ``` , , ``` Certains guides vous recommanderont de lire l'adresse la plus à gauche, mais cela vous rend vulnérable à la [manipulation d'en-têtes](https://adam-p.ca/blog/2022/03/x-forwarded-for/) : ``` , , , ``` Nous préférons lire les adresses depuis la _droite_, en prenant en compte le nombre de proxys. Dans ce cas, nous utiliserions `XFF_DEPTH=3`. > [!NOTE] Si vous avez au contraire besoin de lire l'adresse la plus à gauche (et que vous ne vous > souciez pas de la manipulation d'en-tête) — par exemple, pour offrir un service de > géolocalisation, pour lequel il est plus important que l'adresse IP soit _réelle_ que _de > confiance_, vous pouvez faire cela en inspectant l'en-tête `x-forwarded-for` au sein de votre > application. ### `BODY_SIZE_LIMIT` La taille maximale du body de la requête à accepter en bytes, en incluant ceux traités par streaming. La taille de body peut également être spécifiée avec un suffixe en kilobytes (`K`), mégabytes (`M`), ou gigabytes (`G`). Par exemple, `512K` ou `1M`. Cette valeur vaut par défaut 512kb. Vous pouvez désactiver cette option avec une valeur de `Infinity` (`0` dans d'anciennes versions de l'adaptateur) et implémenter une vérification personnalisée dans la fonction [`handle`](hooks#Server-hooks-handle) si vous avez besoin de quelque chose de plus avancé. ### `SHUTDOWN_TIMEOUT` Le nombre de secondes à attendre avant de fermer les connexions restantes après avoir reçu un signal `SIGTERM` ou `SIGINT`. Vaut par défaut `30`. En interne, l'adaptateur appelle [`closeAllConnections`](https://nodejs.org/api/http.html#servercloseallconnections). Voir la section [Arrêt propre](#Graceful-shutdown) pour plus de détails. ### `IDLE_TIMEOUT` Lors de l'utilisation de l'activation de socket via systemd, `IDEL_TIMEOUT` spécifie le nombre de secondes après lequel l'application est automatiquement mise en sommeil si elle ne reçoit plus de requêtes. Si non précisé, l'application n'est jamais mise en sommeil. Voir la section [Activation de socket](#Socket-activation) pour plus de détails. ## Options L'adaptateur peut être configuré avec différentes options : ```js // @errors: 2307 /// file: svelte.config.js import adapter from '@sveltejs/adapter-node'; /** @type {import('@sveltejs/kit').Config} */ const config = { kit: { adapter: adapter({ // les options par défaut sont affichées out: 'build', precompress: true, envPrefix: '' }) } }; export default config; ``` ### out Le dossier dans lequel compiler le serveur. Vaut par défaut `build` — c-à-d que la commande `node build` va pouvoir lancer le serveur localement une fois la compilation terminée. ### precompress Active la précompression en utilisant gzip et brotli pour les assets et les pages pré-rendues. Vaut par défaut `true`. ### envPrefix Si vous avez besoin de changer le nom des variables d'environnement utilisées pour configurer le déploiement (par exemple, pour éviter les conflits avec les variables d'environnement que vous ne contrôlez pas), vous pouvez préciser un préfixe : ```js envPrefix: 'MY_CUSTOM_'; ``` ```sh MY_CUSTOM_HOST=127.0.0.1 \ MY_CUSTOM_PORT=4000 \ MY_CUSTOM_ORIGIN=https://my.site \ node build ``` ## Arrêt propre [!VO]Graceful shutdown Par défaut, l'adaptateur `adapter-node` éteint proprement le serveur HTTP lorsqu'un signal `SIGTERM` ou `SIGINT` est reçu. Il va faire les choses suivantes : 1. rejeter les nouvelles requêtes ([`server.close`](https://nodejs.org/api/http.html#serverclosecallback)) 2. attendre que les requêtes déjà en cours mais n'ayant pas encore reçu de réponse puisse se finir et fermer les connections une fois qu'elles se mettent en attente ([`server.closeIdleConnections`](https://nodejs.org/api/http.html#servercloseidleconnections)) 3. et enfin, fermer toute connection restante qui serait toujours active après [`SHUTDOWN_TIMEOUT`](#Environment-variables-SHUTDOWN_TIMEOUT) > [!NOTE] Si vous souhaitez personnaliser ce comportement, vous pouvez utiliser un [serveur > personnalisé](#Custom-server). Vous pouvez écouter l'évènement `sveltekit:shutdown` qui est émis après que le serveur HTTP a fermé toutes les connexions. À la différence de l'évènement `exit` de Node, l'évènement `sveltekit:shutdown` supporte les opérations asynchrones et est toujours émis lorsque toutes les connections sont fermées même si le serveur a des opérations en cours comme des connexions ouvertes à des bases de données. ```js // @errors: 2304 process.on('sveltekit:shutdown', async (reason) => { await jobs.stop(); await db.close(); }); ``` Le paramètre `reason` a l'une des valeurs suivantes : - `SIGINT` - l'arrêt a été déclenché par un signal `SIGINT` - `SIGTERM` - l'arrêt a été déclenché par un signal `SIGTERM` - `IDLE` - l'arrêt a été déclenché par [`IDLE_TIMEOUT`](#Environment-variables-IDLE_TIMEOUT) ## Activation de socket [!VO]Socket activation La plupart des systèmes d'exploitation Linux d'aujourd'hui utilisent un gestionnaire de processus moderne appelé systemd pour lancer un serveur et gérer les services. Vous pouvez configurer votre serveur pour allouer un socket, démarrer et dimensionner votre application à la demande. Ce processus est appelé [activation de socket](https://0pointer.de/blog/projects/socket-activated-containers.html). Dans ce cas, l'OS va fournir deux variables d'environnement à votre application — `LISTEN_PID` et `LISTEN_FDS`. L'adaptateur va ensuite écouter le "file descriptor 3" qui fait référence à un socket systemd que vous devrez créer. > [!NOTE] Vous pouvez toujours utiliser [`envPrefix`](#Options-envPrefix) avec l'activation de > socket de systemd. `LISTEN_PID` et `LISTEN_FDS` sont toujours lus sans préfixe. Pour tirer profit de l'activation de socket, veuillez suivre ces étapes. 1. Lancer votre application en tant que [service systemd](https://www.freedesktop.org/software/systemd/man/latest/systemd.service.html). Elle peut soit être lancée directement sur le système hôte ou à l'intérieur d'un conteneur (avec Docker ou un service systemd portable par exemple). Si vous passez en plus une variable d'environnement [`IDLE_TIMEOUT`](#Environment-variables-IDLE_TIMEOUT) à votre application, celle sera éteinte proprement lorsqu'elle ne recevra plus de requêtes pendant `IDLE_TIMEOUT` secondes. systemd va automatiquement démarrer de nouveau votre application lorsque de nouvelles requêtes arriveront. ```ini /// file: /etc/systemd/system/myapp.service [Service] Environment=NODE_ENV=production IDLE_TIMEOUT=60 ExecStart=/usr/bin/node /usr/bin/myapp/build ``` 2. Créer une [unité de socket d'accompagnement](https://www.freedesktop.org/software/systemd/man/latest/systemd.socket.html). L'adaptateur accepte uniquement un socket unique. ```ini /// file: /etc/systemd/system/myapp.socket [Socket] ListenStream=3000 [Install] WantedBy=sockets.target ``` 3. Assurez-vous que systemd a reconnu les deux unités en lançant `sudo systemctl daemon-reload`. Puis activez le socket au démarrage et lancez-le immédiatement en utilisant `sudo systemctl enable --now myapp.socket`. L'application va alors automatiquement démarrer lors de la réception de la première requête sur `localhost:3000`. ## Serveur personnalisé [!VO]Custom server L'adaptateur crée deux fichiers dans votre dossier de compilation — `index.js` et `handler.js`. Lancer `index.js` — c-à-d `node build`, si vous utilisez le dossier de compilation par défaut — va lancer un serveur sur le port configuré. Autrement, vous pouvez importer le fichier `handler.js`, qui exporte un gestionnaire pouvant être utilisé avec [Express](https://github.com/expressjs/express), [Connect](https://github.com/senchalabs/connect) ou [Polka](https://github.com/lukeed/polka) (ou même juste avec la méthode intégrée [`http.createServer`](https://nodejs.org/dist/latest/docs/api/http.html#httpcreateserveroptions-requestlistener)), et lancer votre propre serveur : ```js // @errors: 2307 7006 /// file: my-server.js import { handler } from './build/handler.js'; import express from 'express'; const app = express(); // ajout d'une route qui vit séparément de l'application SvelteKit app.get('/healthcheck', (req, res) => { res.end('ok'); }); // laiiser SvelteKit gérer tout le reste, incluant le fait de servir des pages pré-rendues et des // assets statiques app.use(handler); app.listen(3000, () => { console.log('écoute sur le port 3000'); }); ```