Stratégie TanStack Query
Ce document décrit comment TanStack Query peut être introduit dans l'écosystème Rilindra sans fragiliser les applications déjà migrées vers le VPS.
L'objectif n'est pas de déplacer la logique métier côté navigateur. L'objectif est de mieux gérer le server state dans les interfaces React : cache, rafraîchissement, mutations, invalidation et états de chargement.
Décision proposée
TanStack Query est pertinent pour Staff Manager en priorité.
Il peut ensuite être étendu à Commu Rilindra si les parcours quiz, leaderboard, rewards et profil joueur ont besoin d'une meilleure synchronisation côté client.
Il ne concerne pas directement bot-rilindra, car le bot est un service runtime Node.js sans interface React. Le bot reste consommé via ses routes internes, ses webhooks, Discord, PostgreSQL et MinIO.
Pourquoi l'ajouter
Staff Manager a beaucoup d'écrans qui manipulent des données serveur :
- tâches staff ;
- utilisateurs et permissions ;
- enchères ;
- VIP ;
- coffres ;
- box de stockage ;
- logs Discord ;
- templates webhook ;
- backups ;
- dashboard bot ;
- grille tarifaire ;
- formulaires publics ;
- données Discord enrichies.
Aujourd'hui, ces vues ont tendance à gérer elles-mêmes :
- les états
loading; - les erreurs ;
- les rechargements manuels ;
- les données obsolètes après mutation ;
- les synchronisations entre plusieurs boutons ou modales ;
- les appels API répétés quand plusieurs composants lisent la même ressource.
TanStack Query apporte une couche standard pour ces problèmes, sans changer la source de vérité.
Source de vérité
La source de vérité reste :
PostgreSQL VPS
API routes Next.js
services serveur
bot Discord
MinIO
Infisical
Coolify
TanStack Query ne doit jamais devenir une source de vérité métier.
Il sert uniquement à synchroniser l'interface avec les données serveur.
Règle centrale
Le navigateur ne parle jamais directement à PostgreSQL, Discord, MinIO ou Infisical.
Le navigateur parle aux routes API.
TanStack Query orchestre les appels API côté client.
Périmètre recommandé
Staff Manager
Staff Manager est la meilleure cible initiale.
Raisons :
- beaucoup de pages sont interactives ;
- beaucoup d'actions modifient des données ;
- plusieurs pages ont besoin de refetch après mutation ;
- les admins doivent avoir une interface stable et prévisible ;
- l'app utilise déjà NextAuth et des routes API internes ;
- les données sont sensibles, donc la logique doit rester côté serveur.
Commu Rilindra
Commu peut bénéficier de TanStack Query dans un second temps.
Cibles possibles :
- session joueur enrichie ;
- quiz disponibles ;
- résultat du dernier quiz ;
- leaderboard ;
- rewards ;
- transactions ;
- profil Discord ;
- historique d'activité.
La migration de Commu doit rester séparée de Staff Manager pour éviter de mélanger deux chantiers.
Bot Rilindra
Le bot n'a pas besoin de TanStack Query.
Les interfaces qui pilotent le bot depuis Staff Manager peuvent utiliser TanStack Query, par exemple :
- état runtime du bot ;
- start / stop / restart ;
- historique backups ;
- logs bot ;
- templates messages ;
- notifications test ;
- queue snapshots ;
- statut Coolify.
Mais le bot lui-même reste indépendant.
Ce que TanStack Query résout
Cache client
Quand plusieurs composants lisent la même donnée, TanStack Query peut partager le résultat au lieu de refaire l'appel.
Exemples :
- profil staff courant ;
- permissions ;
- liste des utilisateurs ;
- état runtime du bot ;
- compteurs du dashboard ;
- catalogue de la grille tarifaire.
Invalidation après mutation
Après une action, l'interface doit savoir quelles données relire.
Exemples :
- créer une tâche invalide
tasks; - valider une tâche invalide
taskset éventuellementdashboard; - modifier un rôle invalide
usersetpermissions; - lancer une sauvegarde invalide
botBackups; - modifier un template invalide
discordTemplates; - annuler une enchère invalide
auctions; - attribuer un coffre invalide
vaults,vipetlogs.
Etats réseau cohérents
Chaque écran peut exposer les mêmes états :
- chargement initial ;
- rechargement silencieux ;
- erreur récupérable ;
- retry ;
- mutation en cours ;
- mutation réussie ;
- mutation échouée.
Moins de logique dupliquée
Les appels fetch, les toasts d'erreur, les refreshs et les invalidations peuvent être factorisés dans des hooks.
Ce que TanStack Query ne résout pas
TanStack Query ne remplace pas :
- PostgreSQL ;
- NextAuth ;
- les routes API ;
- les validations serveur ;
- les permissions ;
- les transactions SQL ;
- les logs métier ;
- les backups ;
- la sécurité des secrets ;
- la gestion Coolify ;
- la logique Discord du bot.
Une mutation TanStack Query n'est pas une transaction métier. La transaction reste côté serveur.
Architecture cible
Provider recommandé
D'après la documentation TanStack Query v5, une application React doit être enveloppée dans un QueryClientProvider.
Dans Next.js App Router, le provider doit être un composant client.
Configuration de départ recommandée pour Rilindra :
staleTime global : 30s à 60s
retry global : limité, par exemple 1 ou 2
refetchOnWindowFocus : désactivé par défaut pour les interfaces admin sensibles
gcTime : valeur par défaut ou ajustée selon les pages
Raison :
- éviter les refetch immédiats après hydratation ;
- éviter de surcharger les API internes ;
- garder une interface admin prévisible ;
- rafraîchir explicitement les données après mutation.
Règles pour les query keys
Les query keys doivent être stables, lisibles et hiérarchiques.
Convention proposée :
["staff", "me"]
["staff", "permissions"]
["users", "list", filters]
["tasks", "list", filters]
["tasks", "calendar", month]
["auctions", "list", filters]
["auctions", "detail", auctionId]
["vip", "tribes", filters]
["vaults", "list", filters]
["storage", "boxes", filters]
["bot", "runtime"]
["bot", "backups", page]
["bot", "discord-templates"]
["priceCatalog", "catalog"]
["priceCatalog", "overrides"]
Règles :
- toujours mettre le domaine en premier ;
- mettre les filtres dans la key ;
- ne jamais mettre de secret dans une query key ;
- éviter les keys vagues comme
["data"]ou["list"]; - utiliser les mêmes keys dans les mutations et les invalidations.
Règles pour les fetchers
Les fetchers doivent être fins.
Ils appellent les routes API et retournent un payload typé.
Ils ne doivent pas :
- construire de SQL ;
- lire de variables serveur ;
- appeler Discord directement ;
- appeler MinIO directement ;
- contenir de logique de permission ;
- masquer une erreur métier importante.
Exemple de structure :
src/lib/api/tasks-client.ts
src/lib/api/users-client.ts
src/lib/api/auctions-client.ts
src/lib/api/bot-client.ts
src/lib/api/vip-client.ts
src/lib/api/price-catalog-client.ts
Règles pour les hooks
Les hooks TanStack Query doivent rester proches du domaine métier.
Exemples :
src/hooks/queries/useTasks.ts
src/hooks/queries/useUsers.ts
src/hooks/queries/useAuctions.ts
src/hooks/queries/useBotRuntime.ts
src/hooks/queries/useVipTribes.ts
src/hooks/queries/usePriceCatalog.ts
Les composants doivent consommer ces hooks plutôt que de réimplémenter fetch.
Mutations et invalidation
Chaque mutation doit définir ses invalidations.
Exemples :
| Mutation | Invalidation minimale |
|---|---|
| Créer une tâche | ["tasks"] |
| Modifier une tâche | ["tasks"] |
| Valider une tâche | ["tasks"], ["staff", "dashboard"] |
| Modifier un rôle utilisateur | ["users"], ["staff", "permissions"] |
| Synchroniser Discord | ["users"], ["staff", "me"] |
| Annuler une enchère | ["auctions"], ["auctions", "detail", id] |
| Attribuer un coffre | ["vaults"], ["vip"], ["bot", "logs"] |
| Lancer backup | ["bot", "backups"], ["bot", "runtime"] |
| Modifier template webhook | ["bot", "discord-templates"] |
| Modifier prix catalogue | ["priceCatalog"] |
Règle :
Une mutation qui change une donnée doit invalider toutes les vues qui affichent cette donnée.
Optimistic updates
Les optimistic updates doivent être rares au début.
Autorisé plus tard :
- bascule d'un toggle UI simple ;
- sélection visuelle ;
- état de filtre local ;
- feedback immédiat sur une action non critique.
À éviter au début :
- validation de tâche ;
- changement de rôle ;
- suppression utilisateur ;
- attribution coffre ;
- annulation d'enchère ;
- start / stop / restart du bot ;
- actions qui ping Discord ;
- actions qui écrivent en base ou dans MinIO.
Pour ces actions, il vaut mieux attendre la réponse serveur puis invalider les queries.
Gestion des erreurs
Les erreurs doivent rester lisibles pour le staff.
Une route API devrait retourner une erreur structurée :
{
"error": "Permission insuffisante",
"code": "FORBIDDEN"
}
TanStack Query doit transformer cela en UI claire :
- toast ;
- message inline ;
- bouton réessayer ;
- action désactivée si nécessaire.
Ne jamais afficher :
- stack trace ;
- token ;
- URL de base contenant identifiants ;
- contenu brut d'une erreur Discord ou Coolify avec secret.
Sécurité
TanStack Query ne change pas le modèle de sécurité.
Les contrôles restent côté serveur :
- session NextAuth ;
- rôle staff ;
- permissions ;
- validation du payload ;
- vérification de l'environnement ;
- secrets uniquement côté serveur ;
- logs sans secret.
Le cache client ne doit pas contenir :
- tokens Discord ;
- secrets Coolify ;
- secrets Infisical ;
- chaînes de connexion DB ;
- URL signées privées trop longues ;
- données qui ne doivent pas survivre à une session.
Données sensibles et cache
Certaines données peuvent être cachées côté client :
- listes de tâches ;
- liste utilisateurs staff ;
- statut public d'un service ;
- historique de backups sans liens sensibles ;
- templates de messages ;
- catalogue prix.
D'autres données doivent être évitées ou très courtes en cache :
- actions bot sensibles ;
- logs contenant des IDs ou messages privés ;
- détails d'erreurs ;
- payloads d'administration ;
- résultats d'opérations de sécurité.
Interaction avec Next.js App Router
Approche recommandée :
Server Components pour les shells de page et les checks serveur.
Client Components pour les surfaces interactives.
TanStack Query dans les Client Components.
Routes API comme frontière de sécurité.
On évite de transformer toute l'application en client-only.
Le provider TanStack Query doit être ajouté au layout ou à un provider global déjà existant.
Interaction avec NextAuth
TanStack Query ne remplace pas NextAuth.
Les routes API doivent continuer à valider la session.
Quand une session expire :
- les queries doivent recevoir un 401 ;
- l'UI doit rediriger ou afficher une action de reconnexion ;
- les mutations doivent échouer proprement.
Interaction avec PostgreSQL VPS
Les hooks ne parlent jamais à PostgreSQL.
Flux attendu :
hook TanStack Query
-> route API Next.js
-> session NextAuth
-> permissions
-> PostgreSQL VPS
-> réponse typée
-> cache TanStack Query
Si une route utilise la session DB app.discord_id, cela reste invisible côté client.
Interaction avec Coolify
Les opérations Coolify restent côté serveur.
TanStack Query peut piloter :
- lecture runtime ;
- déclenchement start ;
- déclenchement stop ;
- déclenchement restart ;
- lecture status ;
- lecture historique backups ;
- test notification.
Mais la route API garde :
- le token Coolify ;
- les validations ;
- les confirmations ;
- les logs d'audit.
Interaction avec Discord
Même principe :
client -> route API -> Discord API
TanStack Query peut gérer :
- loading ;
- erreurs ;
- invalidation ;
- refetch après sync ;
- affichage de résultats.
Il ne doit jamais exposer le token Discord.
Interaction avec MinIO
Pour les images d'enchères et assets :
- les uploads passent par une route API ;
- la route API parle à MinIO ;
- l'interface invalide les queries liées aux images ;
- la rétention reste côté MinIO ou jobs serveur.
TanStack Query peut aider à afficher :
- progression logique ;
- liste des images ;
- statut de purge ;
- erreurs de chargement.
Ordre d'adoption recommandé
Phase 0 - Stabilisation
Attendre que la migration VPS prod soit stable.
Critères :
- logs prod propres ;
- staff prod testé ;
- bot prod OK ;
- backups OK ;
- aucun chantier DB urgent.
Phase 1 - Provider et conventions
Ajouter :
- dépendance
@tanstack/react-query; - provider global ;
- fichier de query keys ;
- helpers fetch JSON ;
- conventions d'erreur ;
- tests de base.
Ne migrer aucune page complexe dans cette phase si possible.
Phase 2 - Page faible risque
Commencer par une page à risque modéré :
/admin/botlecture runtime ;- historique backups ;
- templates simples ;
- page users en lecture seule.
Objectif :
- valider provider ;
- valider conventions ;
- valider invalidation simple ;
- vérifier logs ;
- vérifier UX.
Phase 3 - Mutations admin contrôlées
Ajouter les mutations :
- synchroniser users ;
- notification test ;
- rafraîchir backups ;
- modifier templates.
Les actions dangereuses gardent une confirmation côté UI et côté route API.
Phase 4 - Pages métier principales
Migrer progressivement :
/tasks;/encheres;/vip;/coffre-banque;/box-stockage;/grille-tarifaire.
Une page à la fois.
Phase 5 - Commu Rilindra
Si Staff Manager est stable, étudier Commu :
- quiz ;
- résultats ;
- leaderboard ;
- rewards ;
- profil ;
- transactions.
Priorité par module Staff Manager
| Priorité | Module | Pourquoi |
|---|---|---|
| P1 | Dashboard bot | Beaucoup de refresh, faible dépendance UI profonde |
| P1 | Users | Mutations claires, invalidation simple |
| P1 | Backups | Données paginées, refetch évident |
| P2 | Tasks | Important mais plus sensible |
| P2 | Enchères | Données volumineuses, images, statuts |
| P2 | VIP | Beaucoup de règles métier |
| P3 | Coffres | Actions sensibles, logs staff |
| P3 | Box stockage | Actions sensibles, lifecycle |
| P3 | Grille tarifaire | Beaucoup d'édition, besoin d'un modèle propre |
Exemple de matrice query keys
| Domaine | Query key |
|---|---|
| Staff courant | ["staff", "me"] |
| Permissions | ["staff", "permissions"] |
| Users | ["users", "list", filters] |
| Tasks liste | ["tasks", "list", filters] |
| Tasks calendrier | ["tasks", "calendar", month] |
| Auctions liste | ["auctions", "list", filters] |
| Auction détail | ["auctions", "detail", id] |
| VIP tribus | ["vip", "tribes", filters] |
| Vaults | ["vaults", "list", filters] |
| Storage boxes | ["storage", "boxes", filters] |
| Bot runtime | ["bot", "runtime"] |
| Bot backups | ["bot", "backups", page] |
| Templates bot | ["bot", "templates", type] |
| Catalogue prix | ["priceCatalog", "catalog"] |
Contrats API à standardiser
Chaque route consommée par TanStack Query devrait tendre vers un format stable.
Succès :
{
"data": {},
"meta": {}
}
Erreur :
{
"error": "Message lisible",
"code": "ERROR_CODE",
"details": {}
}
Pour les listes :
{
"data": [],
"meta": {
"page": 1,
"pageSize": 25,
"total": 120
}
}
Cette standardisation peut être progressive.
Tests à prévoir
Tests unitaires
- query keys ;
- fetchers ;
- gestion erreurs ;
- mutations ;
- invalidations.
Tests composants
- loading ;
- erreur ;
- succès ;
- mutation pending ;
- invalidation après succès.
Tests intégration UI
- page chargée ;
- action réalisée ;
- donnée rafraîchie ;
- pas de double action ;
- pas de 500.
Tests production staging
- vérifier logs API ;
- vérifier absence de refetch excessif ;
- vérifier pagination ;
- vérifier session expirée ;
- vérifier boutons dangereux.
Observabilité
Sur les pages migrées, surveiller :
- nombre d'appels API ;
- erreurs 401/403 ;
- erreurs 500 ;
- temps de réponse ;
- mutations doublées ;
- stale data visible ;
- logs staff ;
- logs DB ;
- logs Coolify si action runtime.
Anti-patterns
À éviter :
- appeler
fetchdirectement dans 20 composants différents ; - invalider toutes les queries après chaque mutation ;
- mettre des tokens dans une query key ;
- faire de l'optimistic update sur une action critique ;
- mélanger état local UI et server state ;
- utiliser TanStack Query pour stocker un formulaire en cours de saisie ;
- mettre la logique métier dans le hook ;
- faire un refetch automatique trop agressif ;
- convertir toute l'application d'un coup.
Décisions à prendre avant implémentation
Avant de coder, décider :
- où placer le provider ;
- si on installe seulement
@tanstack/react-queryou aussi les devtools en dev ; - conventions de query keys ;
- format de réponse API cible ;
- première page pilote ;
- stratégie toast et erreurs ;
- staleTime global ;
- règles pour refetchOnWindowFocus ;
- politique optimistic updates ;
- limites de cache pour données sensibles.
Première page pilote recommandée
La meilleure première page est probablement le Dashboard Bot.
Pourquoi :
- déjà très orienté API ;
- beaucoup de données runtime ;
- refresh actuel utile ;
- actions séparables ;
- risque métier plus contrôlable si les actions dangereuses gardent leurs confirmations ;
- impact visible immédiatement.
Plan pilote :
- query
["bot", "runtime"]; - query
["bot", "backups", page]; - mutation
restartBotavec confirmation ; - invalidation
["bot", "runtime"]; - mutation
sendTestNotification; - tests composant et route ;
- vérification staging.
Critères de réussite
TanStack Query apporte de la valeur si :
- moins de code de chargement dupliqué ;
- moins de refresh manuel ;
- moins d'états incohérents après mutation ;
- moins d'appels API inutiles ;
- erreurs plus claires ;
- UX plus fluide ;
- tests plus simples à écrire.
Si une page devient plus complexe après migration, il faut ralentir et revoir les abstractions.
Critères de refus temporaire
Ne pas commencer si :
- la prod vient de recevoir une migration critique non validée ;
- les logs prod ne sont pas propres ;
- les routes API d'une page ne sont pas stabilisées ;
- les réponses API sont trop hétérogènes ;
- une page a encore une dette métier importante ;
- les tests de base ne passent pas.
Résumé opérationnel
TanStack Query est un bon choix pour Staff Manager.
On l'utilise pour server state côté client.
On garde PostgreSQL, Discord, MinIO, Coolify et Infisical côté serveur.
On migre par petites surfaces.
On commence par Dashboard Bot ou Users.
On évite les optimistic updates sur les actions critiques.
On standardise query keys, fetchers, mutations et invalidations.
Références
- TanStack Query v5, documentation React : provider
QueryClientProvider,QueryClient,staleTime, mutations et invalidation. - Documentation Next.js App Router dans TanStack Query : provider client et stratégie SSR/hydratation.
- Conventions internes Rilindra : PostgreSQL VPS comme source de vérité, API routes comme frontière de sécurité, Infisical/Coolify pour les secrets et le runtime.