La gestion des erreurs est la pierre angulaire des applications JavaScript robustes et fiables, distinguant un code d'expérimentation d'une solution prête pour la production. Une erreur non gérée peut non seulement faire planter un serveur Node.js, mais aussi laisser une interface utilisateur dans un état incohérent, ou pire encore, corrompre silencieusement des données critiques. Une stratégie d'erreur bien pensée anticipe les défaillances potentielles, permet une récupération élégante et fournit des informations claires, tant aux utilisateurs qu'aux développeurs, sur la nature du problème rencontré.

Comprendre try/catch/finally

Gestion Basique des Erreurs
try {
  const data = JSON.parse(userInput);
  processData(data);
} catch (error) {
  // Handle the error
  console.error("Failed to parse input:", error.message);
  showUserMessage("Invalid input. Please check your data.");
} finally {
  // Always runs — cleanup code
  hideLoadingSpinner();
}

Les Différents Types d'Erreurs en JavaScript

Types d'Erreurs Intégrés
// SyntaxError — invalid JSON, malformed code
JSON.parse("{invalid}");

// TypeError — wrong type, accessing property of null/undefined
null.toString();
undefined.map(x => x);

// ReferenceError — using an undeclared variable
console.log(nonExistentVariable);

// RangeError — value outside allowed range
new Array(-1);

// URIError — malformed URI
decodeURIComponent("%");

// Check error type
try {
  riskyOperation();
} catch (error) {
  if (error instanceof TypeError) {
    // Handle type errors specifically
  } else if (error instanceof SyntaxError) {
    // Handle syntax errors
  } else {
    throw error; // Re-throw unexpected errors
  }
}

Créer des Classes d'Erreurs Personnalisées

Alors que les types d'erreurs intégrés couvrent des scénarios fondamentaux, vos applications bénéficieront grandement de classes d'erreurs personnalisées. Elles permettent d'ajouter un contexte spécifique à l'application, comme des codes d'état HTTP ou des identifiants d'erreur métier, facilitant ainsi la distinction entre les erreurs opérationnelles (attendues, comme "ressource introuvable") et les erreurs de programmation (bogues nécessitant une correction). Cela améliore la lisibilité, la débogabilité et la capacité de votre code à réagir de manière différenciée aux problèmes.

Erreurs Personnalisées
class AppError extends Error {
  constructor(message, statusCode, code) {
    super(message);
    this.name = 'AppError';
    this.statusCode = statusCode;
    this.code = code;
    this.isOperational = true;
  }
}

class NotFoundError extends AppError {
  constructor(resource = 'Resource') {
    super(`${resource} not found`, 404, 'NOT_FOUND');
    this.name = 'NotFoundError';
  }
}

class ValidationError extends AppError {
  constructor(field, message) {
    super(`Validation failed: ${message}`, 400, 'VALIDATION_ERROR');
    this.name = 'ValidationError';
    this.field = field;
  }
}

// Usage
function getUser(id) {
  const user = db.findUser(id);
  if (!user) throw new NotFoundError('User');
  return user;
}

Gestion des Erreurs dans le Code Asynchrone

La gestion des erreurs dans les opérations asynchrones est cruciale. Avec async/await, l'utilisation de try/catch devient intuitive, mimant le comportement du code synchrone. Cependant, il est essentiel de s'assurer que toutes les promesses sont soit résolues, soit rejetées et gérées, car un rejet de promesse non intercepté peut conduire à des situations imprévues et potentiellement à des plantages d'application, surtout en environnement Node.js. L'exemple ci-dessus illustre comment encapsuler les appels réseau et les défaillances potentielles.

Gestion des Erreurs avec Async/Await
// ✅ Correct — try/catch with async/await
async function fetchUser(id) {
  try {
    const response = await fetch(`/api/users/${id}`);

    if (!response.ok) {
      throw new AppError(
        `HTTP ${response.status}: ${response.statusText}`,
        response.status,
        'HTTP_ERROR'
      );
    }

    return await response.json();
  } catch (error) {
    if (error instanceof AppError) {
      throw error; // Re-throw known errors
    }
    // Wrap unknown errors
    throw new AppError('Network error', 500, 'NETWORK_ERROR');
  }
}

// ❌ Wrong — unhandled promise rejection
async function bad() {
  const data = await fetch('/api/data'); // If this throws, it's unhandled!
}

Gestionnaires d'Erreurs Globaux : Le Filet de Sécurité

Malgré toutes les précautions, des erreurs inattendues peuvent toujours se produire et échapper aux blocs try/catch locaux. C'est là que les gestionnaires d'erreurs globaux entrent en jeu, agissant comme un filet de sécurité pour attraper les exceptions et les rejets de promesses non gérés. Que ce soit dans un navigateur via window.addEventListener ou sur un serveur Node.js avec process.on, ces mécanismes sont vitaux pour journaliser les problèmes, prévenir les plantages abrupts et informer les utilisateurs d'un problème général.

Gestionnaires d'Erreurs Génériques
// Browser — catch unhandled errors
window.addEventListener('error', (event) => {
  console.error('Uncaught error:', event.error);
  // Send to error tracking service
});

window.addEventListener('unhandledrejection', (event) => {
  console.error('Unhandled promise rejection:', event.reason);
  event.preventDefault(); // Prevent default browser handling
});

// Node.js — catch unhandled errors
process.on('uncaughtException', (error) => {
  console.error('Uncaught exception:', error);
  process.exit(1); // Exit — the process is in an unknown state
});

process.on('unhandledRejection', (reason) => {
  console.error('Unhandled rejection:', reason);
  // In Node 15+, this terminates the process by default
});

Middleware d'Erreurs Centralisé avec Express.js

Dans les applications backend construites avec Express.js, la centralisation de la gestion des erreurs est une bonne pratique essentielle. Un middleware d'erreur dédié, identifiable par ses quatre paramètres (err, req, res, next), permet de traiter toutes les erreurs survenues dans la chaîne de requêtes. Il offre un point unique pour logguer les erreurs en détail côté serveur et envoyer une réponse propre et appropriée au client, évitant ainsi d'exposer des informations sensibles et améliorant l'expérience utilisateur en cas de défaillance de l'API.

Gestionnaire d'Erreurs Centralisé
// Error-handling middleware (4 parameters)
app.use((err, req, res, next) => {
  const statusCode = err.statusCode || 500;
  const message = err.isOperational ? err.message : 'Internal server error';

  // Log the full error
  console.error(`[${err.code}] ${err.message}`, err.stack);

  // Send clean response to client
  res.status(statusCode).json({
    error: {
      message,
      code: err.code || 'INTERNAL_ERROR',
      ...(process.env.NODE_ENV === 'development' && { stack: err.stack })
    }
  });
});

Bonnes Pratiques pour une Gestion d'Erreurs Impeccable

  • Ne jamais ignorer les erreurs silencieusement — Les blocs catch vides sont une source majeure de bogues insidieux, rendant le débogage cauchemardesque.
  • Adopter les classes d'erreurs personnalisées pour faire la distinction claire entre les erreurs opérationnelles (prévisibles, comme une validation échouée) et les erreurs de programmation (bogues inattendus nécessitant une intervention).
  • Toujours relancer les erreurs que vous ne pouvez pas gérer — Permettez-leur de remonter la chaîne d'appels jusqu'à un gestionnaire global capable de les traiter ou de les journaliser de manière adéquate.
  • Journaliser les erreurs avec un contexte riche — Incluez des informations pertinentes comme l'ID utilisateur, l'URL de la requête, les données d'entrée ou l'état de l'application pour faciliter le diagnostic.
  • Utiliser un service de suivi d'erreurs en production — Des outils comme Sentry, Datadog ou LogRocket sont indispensables pour détecter, analyser et réagir aux problèmes en temps réel.
  • Gérer les échecs avec élégance — Plutôt qu'une page blanche ou un crash abrupt, présentez toujours un message d'erreur clair et utile à l'utilisateur, et suggérez des étapes à suivre si possible.

Découvrez Nos Outils JavaScript Gratuits

Déboguez et formatez votre code JavaScript instantanément.