Aller au contenu

Express : dépannage

Fenêtre de terminal
node --version
npm ls express # version d'Express installée
node src/serveur.js # lancer à la main : l'erreur s'affiche
sudo journalctl -u monapi -n 50 --no-pager # service systemd
curl -i http://127.0.0.1:3000/sante
sudo ss -tlnp | grep 3000
Message Cause et solution
Cannot find module 'express' npm install non exécuté, ou mauvais dossier
Cannot use import statement outside a module "type": "module" absent de package.json
ERR_MODULE_NOT_FOUND En modules ES, extension manquante dans l’import relatif (./routes/clients.js)
EADDRINUSE Port déjà utilisé : sudo ss -tlnp | grep 3000
EACCES: permission denied sur le port Port inférieur à 1024 sans droit : utiliser un port élevé et un reverse proxy
TypeError: Missing parameter name at … Chemin Express 4 (*, /:id?) : voir la migration vers Express 5
SyntaxError à l’import d’un module Version de Node.js trop ancienne pour la syntaxe : node --version
Symptôme Cause et solution
Cannot GET /chemin Route absente ou méthode différente (POST au lieu de GET)
req.body vaut undefined express.json() manquant, déclaré après la route, ou Content-Type absent
400 avec entity.parse.failed Corps JSON invalide
413 Corps trop gros : limit, ou limite du reverse proxy
req.query contient un tableau Paramètre répété : valider le type
Route générique qui masque une autre Ordre de déclaration
Cannot set headers after they are sent to the client Plusieurs réponses : utiliser return res..., vérifier qu’aucun next() n’est appelé après une réponse
La requête ne se termine jamais Middleware sans next() ni réponse
404 pour un fichier statique Chemin du dossier public (chemin absolu), ordre des middlewares

L’erreur « blocked by CORS policy » apparaît dans la console du navigateur, pas côté serveur :

import cors from 'cors';
app.use(
cors({
origin: ['https://exemple.com'],
methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization'],
}),
);

Vérifier la requête préliminaire :

Fenêtre de terminal
curl -i -X OPTIONS http://127.0.0.1:3000/api/clients \
-H 'Origin: https://exemple.com' -H 'Access-Control-Request-Method: POST'

La réponse doit contenir Access-Control-Allow-Origin.

Avec Express 5, une erreur dans un gestionnaire async est transmise au gestionnaire d’erreurs. Elle ne l’est pas si elle survient dans un rappel (callback) ou un setTimeout :

app.get('/x', (req, res) => {
setTimeout(() => {
throw new Error('perdue');
}, 10); // plante le processus
res.send('ok');
});

Capturer les erreurs dans ces cas ou utiliser des promesses (await). Filets de sécurité :

process.on('unhandledRejection', (raison) => {
console.error('rejet non géré', raison);
});
process.on('uncaughtException', (err) => {
console.error('exception', err);
process.exit(1);
});

Après une uncaughtException, quitter le processus : le superviseur (systemd, PM2) le relance dans un état sain.

Fenêtre de terminal
node --inspect src/serveur.js # profilage avec les outils de développement du navigateur
node --prof src/serveur.js # profil de performance
ps -o pid,rss,cmd -C node # mémoire du processus
Symptôme Piste
Mémoire qui croît Cache ou tableau global qui grossit, sessions en mémoire, fuite d’écouteurs d’événements
Réponses lentes Requêtes SQL sans index (MariaDB : index et performances), boucle bloquante, readFileSync dans une route
Un cœur saturé, les autres libres Node.js est mono-thread : mode cluster (PM2)
JavaScript heap out of memory Fuite, ou traitement de grosses données en mémoire : flux (streams), pagination

Éviter dans les gestionnaires les fonctions synchrones qui bloquent (fs.readFileSync, crypto.pbkdf2Sync, boucles longues) : elles figent toutes les requêtes.

Symptôme Piste
req.ip vaut 127.0.0.1 app.set('trust proxy', 1)
req.protocol vaut http trust proxy et X-Forwarded-Proto
Redirection vers http:// Idem
Cookie secure non enregistré trust proxy, HTTPS effectif
Limitation de débit commune à tous trust proxy
Fenêtre de terminal
npm install express@5
npm ls express

Rechercher dans le code les points suivants :

Fenêtre de terminal
grep -rnE "app\.del\(|req\.param\(|res\.redirect\('back'|\.send\([0-9]{3}\)" src/
grep -rnE "['\"]/[^'\"]*\*[^'\"]*['\"]|\?['\"]" src/ | head
À corriger Remplacement
app.del() app.delete()
req.param('x') req.params.x, req.body.x ou req.query.x
res.send(404) res.sendStatus(404) ou res.status(404).send(...)
Chemin * /*splat
Chemin /:id? {/:id}
Expression régulière dans un chemin Valider dans le gestionnaire
req.body supposé {} req.body ?? {}

Un try/catch + next(err) existant continue de fonctionner.

Fenêtre de terminal
node --test # lanceur intégré
node --test --watch

Un test qui ne se termine pas : serveur non fermé (serveur.close()), connexion à la base ouverte (pool.end()).

Ubuntu 24.04 et 26.04 : différences à connaître

Section intitulée « Ubuntu 24.04 et 26.04 : différences à connaître »

Les diagnostics sont identiques sur les deux versions. Vérifier que la version de Node.js installée satisfait Express 5 (18 ou plus) ; les mises à jour d’Ubuntu n’affectent pas un Node.js installé avec nvm.