-
Notifications
You must be signed in to change notification settings - Fork 7
Dépannage fr FR
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
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 :
-
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.tsetsrc/react/routes.tsxgarantit que vous savez toujours comment le routage est câblé. -
Code vs Intention :
make:cloneduplique 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.
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 :
-
schema.safeParse(req.body)valide uniquement les champs non fiables soumis par le client (ex:{ title: "Mon item" }). -
inject(req)injecte le contexte serveur de confiance (ex:{ user_id: req.me.id }). -
req.bodyest 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
BigInten 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.
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 arrayImportant
make:clone crée les fichiers mais ne les enregistre pas automatiquement. Vous devez ajouter l'import vous-même.
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 :
- Ajoutez un en-tête :
x-csrf-token: test-token - Ajoutez un cookie :
__Host-x-csrf-token=test-token
Les valeurs doivent correspondre. Leur contenu n'a pas d'importance pour les tests.
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.
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 :
- Exécuter
npm run database:resetpour recréer la base de données - Mettre à jour le type dans
src/types/index.d.ts - Mettre à jour le schéma Zod dans le repository
- 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.
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 :
-
tests/fixtures/items.ts: ajoutez le nouveau champ à chaque objet fixture -
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.
Symptôme : vos modifications de schéma ne prennent pas effet après avoir exécuté database:reset.
Causes possibles :
-
Erreur de syntaxe en SQL : vérifiez votre
schema.sqlpour détecter d'éventuelles coquilles. SQLite s'arrêtera silencieusement à la première erreur. -
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. - Mauvais chemin de base de données : assurez-vous d'exécuter la commande depuis la racine du projet.
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-hereCela garantit que les JWT signés avant un redémarrage restent valides. Redémarrez le serveur pour que la modification prenne effet.
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:587En développement, si SMTP_URL n'est pas définie, le lien magique est affiché dans la console à la place.
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
}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.
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.
- 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.
Co-création IA
Bien démarrer
Explications
Guides
Référence
Aller plus loin