2. Le problème des états globaux sur le serveur
Les états globaux sont pratiques car ils permettent de stocker des données réactives accessibles par tout composant d'une application Svelte.
Par état global on désigne ici aussi bien un store de Svelte 4, ou qu'un état global
$state
Svelte 5, comme vu dans ce chapitre,
Mais cela peut se révéler problématique avec SvelteKit, car SvelteKit permet de travailler sur le serveur.
Voyons un piège classique lié à l'utilisation d'un état global avec SvelteKit : les données de user.
On souhaite pouvoir accéder aux données du user partout dans notre application. Un état global
user
semble donc adapté.
1. Je crée un état global
Supposons que l'on crée un état global user
contenant les informations d'une personne. Un état
globale est pertinent car on souhaite afficher ces valeurs à différents endroits de la page.
// stores.svelte.ts
function createUser() {
let score = $state();
function set(u) {
user = u;
}
return {
get value() {
return score;
},
set
};
}
export const user = createUser(); // l'état `user` est instancié une fois pour toute l'application
Ce état global est accessible partout, dans tous les composants ainsi que dans tous les fichiers
.ts
ou .js
, ce qui est pratique.
Mais si vous utilisez les options par défaut de SvelteKit, vous avez du rendu côté serveur, ce qui signifie que vos pages sont potentiellement d'abord construites sur le serveur. Et donc que votre état global sera instancié sur le serveur, et maintenu tant que le processus Node tourne.
2. Je mets à jour mon état global sur le client
Ça ne pose probablement pas de problème si ce store n'est jamais vraiment utilisé sur le serveur, car en général on met à jour ce genre de données sur le client lors d'interactions comme des clics.
<script>
import { user } from '$lib/stores.svelte';
</script>
<!-- ici on est sûr de mettre à jour `user` uniquement sur le client -->
<button onclick={() => user.set({ name: 'romain' })}>Remplir le store</button>
La valeur du état global instancié sur le serveur reste donc à sa valeur initiale – undefined
dans
notre cas, ce qui ne pose pas vraiment de problème autre qu'un peu de mémoire inutilement allouée.
3. Je récupère des données depuis le serveur
Imaginez que vous récupériez le score de la personne depuis le serveur, par exemple grâce à la
fonction load
d'un +layout.server.ts
.
// +layout.server.ts
export async function load() {
const user = await getUserData(); // on devrait utiliser les cookies, mais on simplifie ici
return {
user
};
}
4. Je mets à jour mon état global dans le layout
Puis, pour rendre cette valeur accessible et manipulable par tous les composants, vous stockez cette
valeur dans votre état global dès que vous la recevez côté client, donc dans le +layout.svelte
.
<!-- +layout.svelte -->
<script>
import { user } from '$lib/stores.svelte';
const { data } = $props();
user.set(data.user);
</script>
Tout va bien, vous avez les données de la personne accessible partout côté client. Mais vous avez
également stocké ces données côté serveur, car le fichier +layout.svelte
est exécuté côté
serveur lors de la première requête...
5. J'ai perdu
Sans le savoir, vous avez rendu accessibles des données personnelles à toute personne se connectant sur votre serveur SvelteKit et qui tenterait de lire le contenu de ce état global.
Même si le contenu de l'état global sur le serveur est écrasé à chaque nouvelle requête, cela pose un problème de sécurité majeur, car une intrusion sur le serveur permettrait d'accéder à la valeur de l'état global à tout moment.
Solutions ?
Il y a plusieurs solutions à ce problème :
onMount
Le problème vient du fait que l'on a mis à jour un état global depuis le serveur. Donc si on fait attention à ne pas mettre à jour cet état depuis le serveur, tout va bien.
Pour cela, on peut utiliser onMount
, qui est une méthode de cycle de
vie uniquement exécutée sur le client.
<!-- +layout.svelte -->
<script>
import { onMount } from 'svelte';
import { user } from '$lib/stores.svelte';
const { data } = $props();
onMount(() => {
user.set(data.user); // tout va bien, je suis côté client
});
</script>
Néanmoins, cela reste une mauvaise idée :
- la possibilité de mettre à jour ailleurs cet état global tout en étant côté serveur reste ouverte
- les éléments nécessitant les données du user ne sont pas rendus côté serveur, nécessitant un re-rendu côté client
Les données de layout sont accessibles partout
On souhaite rendre disponibles nos données de user
dans toute notre application. Il se trouve que
les données de layout sont rendues disponibles
par SvelteKit dans toutes les pages concernées par le layout.
En utilisant cette propriété sur le layout racine – qu'il est impossible d'esquiver –, il est donc possible de rendre disponibles des données dans toutes les pages de l'application.
<!-- +page.svelte -->
<script>
import { onMount } from 'svelte';
const { data } = $props(); // `data` contient les données de layout
</script>
<p>{data.user.name}</p>
Il faudra tout de même passer ces informations aux composants qui en ont besoin, ou bien utiliser le store de page qui contient également les données de layout, sans présenter de risque de fuites de données.
Bien sûr cette dernière solution ne permet pas telle quelle de mettre à jour nos données dans toute
l'application comme le ferait un état global. Néanmoins, on peut supposer que la mise à jour de
telles données – celles de l'utilisateur dans notre cas – vont nécessiter une requête au serveur. En
utilisant les fonctionnalités des fonctions
load
, on peut assez facilement mettre à jour
nos données globalement sans utiliser d'état global.
État global et Contexte
La solution ci-dessus, pourtant adaptée, ne vous satisfait pas ? Vous souhaitez tout de même pouvoir mettre à jour vos données globalement côté client sans nécessiter un appel au serveur ? Bien.
Il existe une technique pour utiliser les états globaux de manière sécurisée, grâce aux Contextes de Svelte, comme montré dans cette vidéo. Il s'agit néanmoins d'une technique avancée que nous ne traiterons pas ici.
En conclusion
De manière générale, avec SvelteKit, évitez d'utiliser des états globaux pour stocker de l'information sensible.
Passez plutôt par les données de layout.