Skip to content

Dépannage fr FR

rocambille edited this page Aug 1, 2026 · 6 revisions

Résumé : Cette page vous aide à répondre aux questions les plus fréquentes à la fois sur l'architecture de StartER et sur des erreurs courantes.

Ce que vous apprendrez :

  • Comprendre les choix d'architecture fondamentaux de StartER
  • Diagnostiquer et corriger les erreurs courantes de rendu, de routage et de schéma
  • Dépanner rapidement les problèmes de SSR, de CSRF et d'authentification

Idées reçues et choix d'architecture

Avant de déboguer des erreurs opérationnelles, comprendre pourquoi StartER est conçu ainsi permet d'éviter les pièges d'architecture les plus fréquents.

Idée reçue 1 : « Pourquoi ne devrais-je pas écrire mes requêtes SQL et mon res.json() directement dans mes routes ? »

Dans les tutoriels Express basiques, il est courant d'écrire des requêtes de base de données et res.json() directement dans les callbacks de route. StartER sépare intentionnellement les responsabilités en trois couches distinctes :

  • Repositories (itemRepository.ts) : accès direct à la base de données et validation Zod au runtime (z.ZodType<Item>), sans dépendance aux requêtes HTTP.
  • Validateurs (itemValidator.ts) : nettoyage et validation des données transmises par le client avant tout traitement.
  • Actions (itemActions.ts) : fonctions courtes (~5 lignes) responsables du statut HTTP et des en-têtes de réponse.

Idée reçue 2 : « Pourquoi make:clone n'enregistre-t-il pas automatiquement les routes ou ne fusionne-t-il pas les modules clonés dans des helpers génériques ? »

StartER adhère strictement à une philosophie Zero-Magic :

  1. Enregistrement explicite des routes : Les scripts d'édition automatique de code par expressions régulières sont fragiles et masquent le flux de contrôle de l'application. Exiger des imports explicites dans src/express/routes.ts et src/react/routes.tsx garantit que vous savez toujours comment le routage est câblé.
  2. Code vs Intention : make:clone duplique du code, pas une intention. Chaque module de domaine cloné (item, task, user) possède son propre cycle de vie métier et ses règles qui évoluent. Forcer une abstraction DRY prématurée sur des modules de domaine distincts crée un couplage fort : modifier un helper générique partagé pour un module risque de casser les autres.

Idée reçue 3 : « Pourquoi user_id n'est-il pas inclus dans le schéma Zod de validation client ? »

Permettre aux clients de soumettre user_id dans le corps des requêtes ouvre la porte à des attaques d'élévation de privilèges (ex: un appelant malveillant forgeant { "user_id": 999 }).

StartER utilise un pipeline de validation en 3 étapes :

  1. schema.safeParse(req.body) valide uniquement les champs non fiables soumis par le client (ex: { title: "Mon item" }).
  2. inject(req) injecte le contexte serveur de confiance (ex: { user_id: req.me.id }).
  3. req.body est remplacé par le résultat assaini et fusionné.

Le schéma Zod client définit uniquement ce que le client est autorisé à transmettre.

Idée reçue 4 : « Pourquoi utiliser node:sqlite synchrone au lieu de promesses asynchrones ou d'un ORM comme Prisma/TypeORM ? »

StartER utilise l'API native node:sqlite de Node.js 22+ en mode synchrone pour des raisons de clarté pédagogique et de performance :

  • Élimine 90 % de la surcharge async/await, des rejets de promesses non gérés et de la magie des requêtes d'ORM.
  • Les méthodes d'accès aux données se lisent comme de pures fonctions TypeScript synchrones.
  • Empêche les plantages de sérialisation BigInt en convertissant les identifiants SQLite en nombres standard (type RowId = number).

Idée reçue 5 : « Pourquoi la protection CSRF renvoie-t-elle 401 Unauthorized au lieu de 403 Forbidden ? »

Lorsqu'un en-tête CSRF est manquant ou ne correspond pas, le middleware csrf de StartER renvoie 401 Unauthorized au lieu de 403 Forbidden.

C'est un choix de sécurité délibéré : renvoyer 401 rend les échecs CSRF indiscernables des échecs de JWT de session, empêchant un attaquant de sonder si un cookie de session est valide ou non.


2. Erreurs fréquentes et symptômes

Route introuvable (404 sur un nouvel endpoint)

Symptôme : vous avez créé un nouveau module avec make:clone, mais les requêtes vers votre nouvel endpoint renvoient 404.

Cause : le fichier de routes n'a pas été enregistré dans src/express/routes.ts (backend) ou src/react/routes.tsx (frontend).

Solution :

// src/express/routes.ts
import postRoutes from "./modules/post/postRoutes";

router.use(postRoutes);

// src/react/routes.tsx
import { postRoutes } from "./components/post";
// ... add postRoutes to the children array

Important

make:clone crée les fichiers mais ne les enregistre pas automatiquement. Vous devez ajouter l'import vous-même.

401 Unauthorized sur les requêtes POST / PUT / DELETE

Symptôme : vos requêtes GET fonctionnent, mais toute mutation (POST, PUT, PATCH, DELETE) renvoie 401.

Cause : le jeton CSRF est manquant. StartER utilise un pattern Double-Submit côté client : chaque requête de mutation doit inclure à la fois un cookie et un en-tête correspondant.

Solution pour le navigateur : c'est géré automatiquement par le hook useMutate() dans src/react/helpers/mutate.ts. Si vous faites des requêtes manuellement (ex: avec fetch), utilisez plutôt le hook useMutate.

Solution pour Postman / Insomnia :

  1. Ajoutez un en-tête : x-csrf-token: test-token
  2. Ajoutez un cookie : __Host-x-csrf-token=test-token

Les valeurs doivent correspondre. Leur contenu n'a pas d'importance pour les tests.

req.item est indéfini (convertisseur de paramètre non enregistré)

Symptôme : votre action plante avec Cannot read properties of undefined lors de l'accès à req.item (ou req.post, etc.).

Cause : le convertisseur de paramètre n'est pas enregistré dans le fichier de routes.

Solution : assurez-vous que votre fichier de routes appelle router.param() :

// src/express/modules/post/postRoutes.ts
import postParamConverter from "./postParamConverter";

router.param("postId", postParamConverter.convert);

Sans cette ligne, Express ne sait pas comment convertir le paramètre d'URL :postId en une entité chargée.

Incohérence de schéma après l'ajout d'une colonne

Symptôme : après l'ajout d'une colonne à schema.sql, votre API renvoie des données sans le nouveau champ, ou TypeScript affiche des erreurs de type.

Cause : vous avez mis à jour schema.sql mais avez oublié une ou plusieurs de ces étapes :

  1. Exécuter npm run database:reset pour recréer la base de données
  2. Mettre à jour le type dans src/types/index.d.ts
  3. Mettre à jour le schéma Zod dans le repository
  4. Mettre à jour les requêtes SQL dans le repository (SELECT, INSERT, UPDATE)

Solution : suivez la liste de contrôle complète (voir Premiers exercices, Exercice 4). Après avoir modifié le type dans index.d.ts, laissez TypeScript vous guider : le compilateur signalera chaque fichier nécessitant une mise à jour.

Les tests échouent après la modification d'une ressource

Symptôme : npm run test échoue après l'ajout ou la modification d'un champ sur une ressource.

Cause : les fixtures de test et les contrats ne connaissent pas le nouveau champ.

Solution : mettez à jour les deux :

  1. tests/fixtures/items.ts : ajoutez le nouveau champ à chaque objet fixture
  2. tests/contracts/items.ts : ajoutez le nouveau champ au corps (body) de chaque requête de mutation (add, edit)

Voir Premiers exercices, Exercice 5 pour un guide pas à pas.

npm run database:reset ne semble pas fonctionner

Symptôme : vos modifications de schéma ne prennent pas effet après avoir exécuté database:reset.

Causes possibles :

  1. Erreur de syntaxe en SQL : vérifiez votre schema.sql pour détecter d'éventuelles coquilles. SQLite s'arrêtera silencieusement à la première erreur.
  2. Serveur toujours en cours d'exécution : certains systèmes d'exploitation verrouillent le fichier de base de données. Arrêtez le serveur de dev, exécutez database:reset, puis redémarrez.
  3. Mauvais chemin de base de données : assurez-vous d'exécuter la commande depuis la racine du projet.

Erreurs JWT / Authentification en développement

Symptôme : après le redémarrage du serveur, vous obtenez un 401 sur les requêtes précédemment authentifiées.

Cause : la clé APP_SECRET a été régénérée (ou non définie).

Solution : définissez un APP_SECRET persistant dans votre fichier .env :

APP_SECRET=your-development-secret-here

Cela garantit que les JWT signés avant un redémarrage restent valides. Redémarrez le serveur pour que la modification prenne effet.

Erreur SMTP_URL au démarrage en production

Symptôme : le serveur refuse de démarrer et enregistre une erreur concernant SMTP_URL.

Cause : en production (NODE_ENV=production), StartER exige que SMTP_URL soit définie. C'est un contrôle de sécurité pour garantir que votre application peut réellement envoyer des emails aux utilisateurs.

Solution : définissez SMTP_URL dans votre environnement de production :

SMTP_URL=smtp://user:password@smtp.example.com:587

En développement, si SMTP_URL n'est pas définie, le lien magique est affiché dans la console à la place.

ReferenceError: window is not defined (ou document / localStorage)

Symptôme : le serveur plante lors de la requête initiale avec ReferenceError: window is not defined pendant le SSR.

Cause : accès à des variables globales réservées au navigateur au niveau supérieur du composant ou en dehors de useEffect.

Solution : enveloppez la logique réservée au navigateur dans useEffect (qui s'exécute exclusivement sur le navigateur) ou protégez avec une vérification de type :

// Bon : s'exécute uniquement sur le navigateur client
useEffect(() => {
  const theme = localStorage.getItem("theme");
}, []);

// Autre protection :
if (typeof window !== "undefined") {
  // Exécution côté client uniquement
}

Incohérence d'hydratation React (Text content does not match server-rendered HTML)

Symptôme : avertissement dans la console du navigateur indiquant que le HTML rendu par le serveur ne correspond pas au DOM du client.

Cause : rendu de valeurs dynamiques (ex: new Date().toLocaleTimeString() ou Math.random()) qui produisent des sorties différentes sur le serveur Node et le navigateur client.

Solution : utilisez des props initiales stables ou formatez les dates en utilisant le helper datetime.ts de StartER avec VITE_TIMEZONE.

Erreurs de Fetch réseau pendant le SSR (Absolute URL required)

Symptôme : le loader côté serveur échoue avec TypeError: Only absolute URLs are supported.

Cause : appel fetch("/api/items") relatif direct pendant le rendu serveur sans transmettre les en-têtes ou le contexte d'URL de base.

Solution : utilisez le helper intégré getOrFetch(url) de StartER ou transmettez les en-têtes de requête dans les loaders comme illustré dans src/react/routes.tsx.

Bonnes pratiques

  • Lisez le message d'erreur et le code de statut HTTP avant d'inspecter le code.
  • Le terminal affiche souvent des informations plus détaillées que la console du navigateur, notamment la stack trace complète.
  • Utilisez les erreurs TypeScript comme fil conducteur lors de la modification des types ou interfaces.

Voir aussi

Clone this wiki locally