TypeScript et gestion d’état hybride pour une architecture type-safe
Notre architecture repose sur un backend client type-safe avec DTOs partagés,
combiné à une gestion d’état hybride claire entre données serveur et état
client.
Le ChataigneClient est importé depuis le package @chataigne/client et instancié dans chaque service avec l’authentification :
Copy
import { ChataigneClient } from "@chataigne/client";import { authClient } from "@/lib/auth-client";// Instance locale dans chaque serviceconst client = new ChataigneClient(authClient.$fetch);
Les DTOs sont partagés entre frontend et backend. Toute modification du contrat API provoque une erreur de compilation.
Copy
// ✅ TypeScript vérifie automatiquement les typesconst catalog = await client.location.catalog.create({ name: "Menu Été", locationId: "loc-123",});// ✅ Autocomplétion et validation des propriétésconst catalogs = await client.location.getLocationCatalogs("loc-123");
API cohérente et prévisible
Structure uniforme pour tous les appels :
Copy
// Pattern cohérent pour toutes les ressourcesclient.location.catalog.create(data);client.location.catalog.update(catalogId, name);client.location.catalog.delete(catalogId);client.location.getLocationCatalogs(locationId);
Gestion d'erreurs centralisée
Toutes les erreurs passent par le même mécanisme, facilitant le debugging et la gestion globale :
Copy
// Gestion des erreurs automatique et cohérenteconst catalogs = await client.location.getLocationCatalogs(locationId);return catalogs;
TanStack Query résout les défis du cache et de la synchronisation des données
serveur. Il évite les appels API redondants, garde nos données fraîches
automatiquement, et gère les états de chargement/erreur pour nous.
TanStack Query gère le cache et la synchronisation de toutes les données
provenant du backend client.
Les custom hooks encapsulent la logique TanStack Query et fournissent une interface simple pour nos composants. Ils transforment nos services en hooks réactifs avec cache automatique.Pattern pour récupérer des données via le backend client :
use-catalogs.hook.ts
Copy
import { useQuery } from "@tanstack/react-query";import { getLocationCatalogs } from "../services/catalog.service";export function useCatalogs(locationId: string) {return useQuery({queryKey: ["catalogs", locationId],queryFn: () => getLocationCatalogs(locationId),});}
Les mutations gèrent les opérations de création, modification et suppression. Elles invalident automatiquement le cache pour maintenir la cohérence des données.Pattern pour les mutations avec invalidation du cache :
Nos composants consomment les hooks de données sans connaître les détails de TanStack Query. Ils reçoivent des données typées, des états de chargement et bénéficient automatiquement du cache.Exemple d’intégration simple :
Zustand gère l’état client qui ne vient pas du serveur : préférences
utilisateur, état des interfaces (sidebar ouverte/fermée), filtres temporaires
et données de formulaires en cours de saisie.
Zustand ne doit jamais dupliquer des données serveur. Son rôle est
complémentaire à TanStack Query.
On crée un store Zustand par feature pour isoler son état UI. Cela évite les conflits entre features et facilite la maintenance.Pattern pour l’état UI d’une feature :
Le store global contient les préférences qui affectent toute l’application : thème, langue, paramètres utilisateur. On utilise la persistance pour conserver ces données entre les sessions.Pour l’état vraiment global de l’application :
Voici comment on fait collaborer les trois couches dans un composant réel. Les données serveur viennent de TanStack Query, l’état UI de Zustand, et la logique métier combine les deux.Exemple d’un composant utilisant les trois couches de l’architecture :
• Toujours passer par le backend client pour les appels API• Ne jamais faire d’appels directs aux endpoints• Utiliser les types partagés pour la type safety• Centraliser la gestion des erreurs
2
TanStack Query
• Utiliser exclusivement pour les données serveur• Structurer les query keys de manière cohérente• Invalider le cache après les mutations• Gérer les états de loading et d’erreur
3
Zustand
• Réserver à l’état UI local uniquement• Ne jamais dupliquer les données serveur• Créer un store par feature pour l’isolation• Utiliser la persistance pour les préférences globales
4
Query Keys
• Structure hiérarchique : ["resource", id, "subresource"]• Cohérence dans toute l’application• Éviter les clés trop génériques• Faciliter l’invalidation ciblée