Gestion des erreurs RxJS : Stratégies pratiques

🏷️ Front-end 📅 06/04/2026 14:00:00 👤 Mezgani Said
Rxjs Erreurs Error-Handling Angular
Gestion des erreurs RxJS : Stratégies pratiques

Gestion d'erreurs RxJS : catchError, retry, throwError, errorHandler, logging et user feedback. Stratégies robustes et bonnes pratiques.

Introduction

Gérer les erreurs correctement est crucial pour une application robuste en production. RxJS offre des outils puissants pour capturer, transformer, récupérer et logger les erreurs sans bloquer le flux réactif.

Enjeu : Une erreur non gérée peut terminer l'Observable et bloquer l'interface. Les utilisateurs ne voient rien, et aucune donnée n'est affichée. D'où l'importance de catchError.

catchError() — Intercepter et transformer

catchError() intercepte les erreurs et vous permet de les gérer ou les transformer. C'est votre filet de sécurité :

// Exemple basique
import { catchError } from 'rxjs/operators';
import { throwError, of } from 'rxjs';

this.userService.getUsers().pipe(
    catchError(error => {
        // Option 1 : Logger et relancer l'erreur
        console.error('Erreur lors du chargement :', error);
        return throwError(() => new Error('Impossible de charger les utilisateurs'));

        // Option 2 : Logger et retourner une valeur par défaut
        // return of([]);  // Retourne un tableau vide

        // Option 3 : Logger et afficher un message à l'utilisateur
        // this.showNotification('Erreur: ' + error.message);
        // return of([]);
    })
).subscribe(users => {
    this.users = users;  // users = [] si erreur
});
Points clés :
  • throwError() propage l'erreur vers le subscribe
  • of(defaultValue) récupère l'erreur et continue
  • EMPTY termine silencieusement l'Observable
  • ❌ Sans catchError, une erreur tue l'Observable

retry() — Réessayer automatiquement

retry() réessaye automatiquement si l'Observable échoue. Utile pour les erreurs réseau temporaires :

import { retry, catchError } from 'rxjs/operators';

this.http.get('/api/users').pipe(
    // Réessaye 3 fois avant d'échouer
    retry(3),
    catchError(error => {
        console.error('Échec après 3 tentatives');
        return of([]);
    })
).subscribe(users => {
    this.users = users;
});

// Avec délai entre les tentatives (Angular 16+)
this.http.get('/api/users').pipe(
    retry({
        count: 3,                          // Nombre de tentatives
        delay: 1000,                       // Délai en ms entre les tentatives
        // delay: (error, retryCount) => {  // Délai dynamique (exponential backoff)
        //   return timer(Math.pow(2, retryCount) * 1000);
        // }
    }),
    catchError(error => {
        this.logger.error('Échec après 3 tentatives', error);
        return of([]);
    })
).subscribe();
Quand utiliser retry :
  • ✅ Erreurs réseau temporaires (timeout, connexion perdue)
  • ✅ Services instables avec pics de charge
  • ❌ Erreurs métier (401 Unauthorized, 400 Bad Request) — ne réessayez pas, c'est inutile

Patterns robustes pour la production

Voici une stratégie complète combinant retry, timeout, logging et fallback :

// error.interceptor.ts — Gestion centralisée des erreurs HTTP
import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpRequest, HttpHandler, HttpErrorResponse } from '@angular/common/http';
import { catchError, retry, timeout } from 'rxjs/operators';
import { throwError, timer } from 'rxjs';

@Injectable()
export class ErrorInterceptor implements HttpInterceptor {
    constructor(private logger: LoggerService, private notify: NotificationService) {}

    intercept(req: HttpRequest<any>, next: HttpHandler) {
        return next.handle(req).pipe(
            // Timeout après 10 secondes
            timeout(10000),

            // Réessaye 3 fois avec délai exponentiel
            retry({
                count: 3,
                delay: (error, retryCount) => {
                    // 1s, 2s, 4s
                    return timer(Math.pow(2, retryCount) * 1000);
                }
            }),

            // Capturer et gérer les erreurs
            catchError((error: HttpErrorResponse) => {
                let message = 'Une erreur est survenue';

                if (error.status === 0) {
                    message = 'Problème de connexion réseau';
                } else if (error.status === 401) {
                    message = 'Session expirée. Reconnexion requise';
                    // Rediriger vers login...
                } else if (error.status === 403) {
                    message = 'Accès refusé';
                } else if (error.status === 404) {
                    message = 'Ressource non trouvée';
                } else if (error.status === 500) {
                    message = 'Erreur serveur. Réessai en cours...';
                }

                // Logger pour les développeurs
                this.logger.error(`HTTP ${error.status}`, error);

                // Notifier l'utilisateur
                this.notify.error(message);

                return throwError(() => error);
            })
        );
    }
}

Logging et debugging

Utilisez tap() pour logger sans affecter le flux. C'est utile pour déboguer :

import { tap } from 'rxjs/operators';

// Tracer le flux complet
this.http.get('/api/users').pipe(
    tap({
        next: (users) => console.log('✓ Utilisateurs reçus :', users.length),
        error: (error) => console.error('✗ Erreur :', error),
        complete: () => console.log('✓ Requête terminée')
    }),
    retry(1),
    catchError(error => {
        return of([]);
    })
).subscribe(users => {
    this.users = users;
});

// RxJS Debugger Chrome — Visualiser le flux en temps réel
// npm install rxjs-devtools
import { debug } from 'rxjs-devtools/operators';

this.http.get('/api/users').pipe(
    debug('USERS'),  // Affiche dans la console RxJS Debugger
    catchError(error => of([]))
).subscribe();
Pro tip : Activez DevTools RxJS dans Chrome pour visualiser chaque émission, erreur et unsubscribe en temps réel. Invaluable pour déboguer les problèmes complexes.

Conclusion

Une bonne gestion d'erreurs rend votre application production-ready. Combinaison gagnante :

  • timeout() pour éviter les requêtes qui traînent
  • retry() pour les erreurs réseau temporaires
  • catchError() pour un fallback utilisateur
  • tap() pour logger sans pollution
  • HttpInterceptor centralisé pour une logique partagée
À retenir : Chaque Observable qui fait une requête réseau doit avoir un catchError(). Pas d'exception à cette règle.