La configuration peut faire ou défaire une API REST. De petits écarts dans les timeouts, le CORS, les limites de taille de corps, l'authentification ou les pools de connexions peuvent entraîner des échecs intermittents difficiles à tracer. Ce guide se concentre sur les erreurs pratiques, la validation sécurisée des changements, la procédure de rollback et le dépannage rapide. Les exemples utilisent Express sur Node.js, MongoDB, ainsi que des intégrations comme Stripe et l'API OpenAI.
Pour qui est ce guide
- Développeurs qui font grandir un service Express ou Node.js
- Consultants DevOps standardisant les configurations API entre services
- Équipes de startup ajoutant des intégrations tierces sous pression de temps
Ce que vous obtenez
- Une liste de contrôle des erreurs courantes de configuration d'API REST
- Des exemples concrets de code et de configuration
- Un workflow de changement sécurisé, un plan pilote réduit et l'habitude du rollback
---
Erreurs courantes de configuration avec exemples pratiques
Voici des erreurs observées en production, avec les correctifs applicables.
1) CORS : défauts permissifs ou allowlists incohérentes
Symptôme :
- Tout fonctionne en tests locaux mais les navigateurs échouent avec des erreurs CORS opaques.
Mauvais :
// Express
const cors = require('cors');
app.use(cors()); // Autorise n'importe quelle origine, risqué et peut masquer des bugs
Mieux (allowlist explicite et méthodes) :
const cors = require('cors');
const allowed = [
'https://app.example.com',
'https://admin.example.com'
];
app.use(cors({
origin: (origin, cb) => {
if (!origin || allowed.includes(origin)) return cb(null, true);
return cb(new Error('Not allowed by CORS'));
},
methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'],
credentials: true
}));
Validation :
- Vérifier le preflight :
curl -i -X OPTIONS https://api.example.com/v1/items -H "Origin: https://app.example.com" -H "Access-Control-Request-Method: GET" - Confirmer que les origines non autorisées sont rejetées avec 403 ou une erreur claire.
Rollback :
- Conserver la configuration CORS précédente dans le contrôle de version ; revenir en arrière si les flux de connexion cassent.
2) Limites du body parser : accepter trop ou trop peu
Symptôme :
- Les utilisateurs uploadant des payloads JSON voient des erreurs 413 (trop volumineux) ou les requêtes restent bloquées.
Correctif :
const express = require('express');
const app = express();
app.use(express.json({ limit: '1mb' }));
app.use(express.urlencoded({ extended: false, limit: '1mb' }));
Astuce :
- Aligner les limites du reverse proxy (par exemple
client_max_body_sizedans nginx) avec les limites Express.
Validation :
- Uploader un payload juste sous la limite et juste au-dessus ; confirmer 200 vs 413.
Rollback :
- Revenir sur le commit de limite et redémarrer seulement le groupe d'instances affecté.
3) Timeouts : réglages client, serveur et proxy mal alignés
Symptômes :
- Erreurs 499/504/408 intermittentes, retentatives de webhook Stripe, ou abandons côté client.
Exemple côté serveur (Node 18+) :
const server = app.listen(process.env.PORT || 3000);
server.requestTimeout = 60000; // 60s pour le cycle de vie complet de la requête
server.headersTimeout = 65000; // 65s pour éviter la course au timeout d'en-têtes
Exemple reverse proxy (nginx) :
# Assurer l'alignement des timeouts proxy et app (valeurs d'exemple)
proxy_connect_timeout 5s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
Appels tiers :
- Définir aussi les timeouts sortants du client HTTP, pour que votre API ne bloque pas sur des upstreams lents. Par exemple, lors d'appels à OpenAI, définir un timeout de 30 secondes sur le client HTTP.
Validation :
- Simuler des handlers lents et confirmer que le système retourne un timeout contrôlé.
Rollback :
- Restaurer les valeurs de timeout précédentes et redéployer seulement la configuration, pas le code.
4) Rate limiting : absent, incohérent ou trop strict
Symptômes :
- Les utilisateurs font quelques requêtes en rafale et voient des 429 ; ou votre API est ouverte aux abus.
Correctif avec Express :
const rateLimit = require('express-rate-limit');
app.use('/v1/', rateLimit({
windowMs: 60 * 1000,
max: 120,
standardHeaders: true,
legacyHeaders: false
}));
Validation :
- Envoyer 130 requêtes en 60s depuis une IP ; confirmer 120 succès, 10 en 429.
Rollback :
- Augmenter la limite ou la fenêtre ; retirer le middleware si des flux critiques cassent.
5) Auth et secrets : mauvaises clés d'environnement ou vérifications manquantes
Symptômes :
- Échecs de paiement ; échec de vérification de webhook ; appels IA retournent 401.
Gardes pour clés Stripe et OpenAI :
if (process.env.NODE_ENV === 'production') {
if (process.env.STRIPE_SECRET_KEY?.startsWith('sk_test_')) {
throw new Error('Refusing to start with Stripe test key in production');
}
}
if (!process.env.OPENAI_API_KEY) {
throw new Error('OPENAI_API_KEY is required');
}
Vérification de signature webhook (Stripe) :
// Conserver le corps brut pour la vérification de signature
app.post('/webhooks/stripe', express.raw({ type: 'application/json' }), (req, res) => {
const sig = req.headers['stripe-signature'];
try {
const event = stripe.webhooks.constructEvent(
req.body,
sig,
process.env.STRIPE_WEBHOOK_SECRET
);
// traiter l'événement
res.sendStatus(200);
} catch (err) {
return res.status(400).send(`Webhook Error: ${err.message}`);
}
});
Validation :
- Utiliser des événements de test connus et vérifier que les contrôles de signature passent et échouent comme prévu.
Rollback :
- Garder l'ancien secret et l'endpoint actifs jusqu'à ce que le trafic confirme que le nouveau fonctionne.
6) Express derrière un proxy : trust proxy manquant et cookies Secure cassés
Symptômes :
- Les utilisateurs ne restent jamais connectés ; Set-Cookie avec Secure n'est pas honoré.
Correctif :
app.set('trust proxy', 1); // s'il y a exactement un reverse proxy
// ou app.set('trust proxy', true) quand plusieurs proxies sont présents
Assurer les réglages de cookies sécurisés une fois trust proxy correct :
app.use(require('cookie-session')({
name: 'sid',
secret: process.env.SESSION_SECRET,
cookie: { secure: true, sameSite: 'lax', httpOnly: true }
}));
Validation :
- Confirmer que
X-Forwarded-Proto: httpsest défini par le proxy et qu'Express le respecte.
Rollback :
- Revenir sur le changement trust proxy si le comportement des cookies régresse ; inspecter les en-têtes du proxy.
7) Problèmes de connexion et de dimensionnement du pool MongoDB
Symptômes :
- Latence en pics, timeouts pendant les pics de trafic, ou tempêtes de connexions lors des déploiements.
Exemple de connexion :
const { MongoClient } = require('mongodb');
const client = new MongoClient(process.env.MONGODB_URI, {
maxPoolSize: 20,
minPoolSize: 5,
serverSelectionTimeoutMS: 5000,
retryWrites: true
});
Conseils opérationnels :
- Utiliser un client par processus et réutiliser les connexions.
- Définir des tailles de pool raisonnables ; trop grand peut submerger le serveur.
- Confirmer que les index existent pour les requêtes chaudes ; des index mal configurés ressemblent à des bugs d'application.
Validation :
- Test de charge avec trafic en rafale et surveiller l'utilisation des connexions et la latence.
Rollback :
- Restaurer les tailles de pool précédentes ; redémarrer une seule instance d'abord pour observer les effets.
8) Pagination par défaut et taille de réponse
Symptômes :
- Les clients récupèrent trop de données par défaut ; latence et mémoire en hausse.
Correctif :
const DEFAULT_PAGE_SIZE = 50;
const MAX_PAGE_SIZE = 200;
app.get('/v1/items', async (req, res) => {
const page = Math.max(1, parseInt(req.query.page || '1', 10));
const pageSize = Math.min(MAX_PAGE_SIZE, Math.max(1, parseInt(req.query.pageSize || DEFAULT_PAGE_SIZE, 10)));
// requête avec limit/skip basés sur page et pageSize
});
Validation :
- S'assurer que les réponses incluent
total,pageetpageSizepour que les clients puissent s'adapter.
Rollback :
- Si les clients cassent, réintroduire l'ancien défaut et planifier un chemin de migration.
9) Niveaux de journalisation et masquage
Symptômes :
- Soit pas de données pour déboguer les incidents, soit fuites de données sensibles dans les logs.
Correctif :
- Définir
LOG_LEVEL=infoen production et assurer le masquage des tokens. - Échantillonner les logs debug seulement pour des endpoints ciblés pendant les fenêtres d'incident.
Validation :
- Confirmer que les en-têtes sensibles (Authorization) sont masqués.
Rollback :
- Baisser la verbosité si les logs inondent le stockage ou font grimper les coûts.
---
Aperçu du workflow
Adopter un flux simple et répétable pour les changements de configuration.
- Baseline et diff
- Capturer la configuration et les valeurs runtime actuelles (timeouts, limites, clés présentes, en-têtes proxy).
- Décider du changement minimal à faire et pourquoi.
- Changer en un seul endroit
- Modifier un seul contrôle à la fois (par exemple, allowlist CORS sur une route).
- Garder le changement derrière un flag ou un middleware spécifique à la route quand possible.
- Valider avec des vérifications répétables
- Fonctionnel : codes de statut, tailles de payload, preflight CORS, flux d'authentification.
- Non-fonctionnel : latence, taux d'erreur, mémoire, connexions ouvertes.
- Intégration : paiement test Stripe, petite complétion OpenAI, lecture/écriture MongoDB.
- Déployer par étapes
- Commencer avec une instance ou un environnement.
- Surveiller métriques et logs avant d'étendre.
- Observer et décider
- Définir à l'avance seuils de succès et de rollback (par exemple 99,9 % de succès, latence p95 < 300 ms, taux 429 < 1 %).
- Rollback rapide
- Revenir sur les commits de config ou basculer les flags.
- Annoncer le résultat et les prochaines étapes, puis planifier un correctif plus profond si nécessaire.
Cette séparation des étapes réduit les allers-retours et aide les équipes à éviter les changements larges et risqués.
---
Plan pilote local
Objectif : livrer une amélioration étroite et mesurable qu'on peut inspecter sur un poste de travail avant un déploiement plus large.
Exemple de pilote : durcir les limites de taille de corps et ajouter une allowlist CORS sur une ressource.
Périmètre
- Route :
POST /v1/uploads - Changements :
express.json({ limit: '1mb' }), allowlist origines pour l'UI d'upload - Métriques : taux 2xx, distribution 4xx (surtout 413 et 403), latence p95, tailles de payload
Étapes
- Préparer
- Ajouter la config avec défauts sensés et surcharges par variables d'environnement.
- Ajouter métriques ou logs pour observer la taille de requête et l'origine.
- Tester sur un poste de travail
- Vérifications taille de payload :
curl -s -o /dev/null -w "%{http_code}\n" -H "Content-Type: application/json" --data @payload-900kb.json https://api.example.com/v1/uploads
# Attendu 200
curl -s -o /dev/null -w "%{http_code}\n" -H "Content-Type: application/json" --data @payload-1100kb.json https://api.example.com/v1/uploads
# Attendu 413
- Preflight CORS :
curl -i -X OPTIONS https://api.example.com/v1/uploads -H "Origin: https://app.example.com" -H "Access-Control-Request-Method: POST"
- Déploiement progressif
- Activer seulement sur
POST /v1/uploads. - Observer métriques 30-60 minutes ; comparer avant/après.
- Décision
- Si les métriques de succès tiennent et les taux d'erreur ne montent pas, étendre aux routes sœurs.
- Sinon, rollback et analyser les logs pour clients mal alignés ou limites proxy.
Rollback
- Revenir sur le changement de config ; confirmer le comportement précédent en quelques minutes.
- Documenter le problème et les types de clients affectés.
---
Workflows de dépannage
Utiliser ces chemins rapides quand quelque chose casse après un changement de config.
- Confirmer le symptôme
- Endpoint exact, méthode, code de statut et plage horaire.
- Reproduire avec en-têtes
curl -v -H "Origin: https://app.example.com" -H "Accept: application/json" -H "Content-Type: application/json" https://api.example.com/v1/items
- Vérifier en-têtes de réponse (CORS, cache, content-type) et timings.
- Vérifier logs et métriques
- Chercher pics de 4xx/5xx, 429, timeouts, saturation mémoire ou pool de connexions.
- Inspecter les frontières
- Limites et timeouts reverse proxy vs timeouts app.
- Terminaison TLS et en-têtes
X-Forwarded-*.
- Valider les hypothèses tierces
- Stripe : type de clé correspondant à l'environnement ; vérification signature webhook.
- OpenAI : clé API présente ; taille de requête et timeouts dans vos limites.
- Connectivité base de données
- Vérifier taille de pool, timeout de sélection serveur, et authentification dans la chaîne de connexion.
- Rollback dernier recours
- Préférer basculer un flag de config ou revenir sur un commit unique plutôt qu'un hotfix code.
---
Conclusion
De petits changements de configuration explicites battent les éditions larges et risquées. Utilisez des allowlists pour le CORS, définissez des limites de corps et timeouts sensées, dimensionnez correctement les pools de connexions, et protégez les secrets. Validez avec des vérifications répétables, déployez par étapes, et gardez le rollback simple et rapide. Commencez par un pilote étroit, mesurez les effets réels, puis étendez en confiance.