🅰️ Angular Q110 / 128

What is Angular error handling strategy?

AI-Powered Answer ✓ Answered

Angular provides a robust and flexible strategy for handling errors, encompassing both client-side and server-side issues. Effective error handling is crucial for creating resilient applications that can gracefully recover from unexpected situations and provide a good user experience.

Core Principles of Angular Error Handling

Angular's error handling strategy is built around several key mechanisms, primarily the global ErrorHandler service, RxJS operators for asynchronous operations, and HTTP Interceptors for network requests.

1. Global Error Handling with `ErrorHandler`

Angular provides a built-in ErrorHandler service. By default, it simply logs errors to the browser's console. However, you can extend this service to implement custom global error handling logic, such as sending errors to a logging service, displaying a notification to the user, or performing other recovery actions.

typescript
import { ErrorHandler, Injectable } from '@angular/core';

@Injectable()
export class CustomErrorHandler implements ErrorHandler {
  handleError(error: any) {
    console.error('An error occurred:', error);
    // Log error to a remote service
    // Display a user-friendly message
  }
}

To use a custom error handler, you must provide it in your AppModule:

typescript
import { NgModule, ErrorHandler } from '@angular/core';
import { CustomErrorHandler } from './custom-error-handler';

@NgModule({
  providers: [
    { provide: ErrorHandler, useClass: CustomErrorHandler }
  ]
})
export class AppModule { }

2. Asynchronous Error Handling with RxJS (`catchError`)

Angular applications heavily rely on Observables for asynchronous operations, especially HTTP requests. The catchError operator from RxJS is the primary way to handle errors originating from these observables.

typescript
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';

@Injectable({ providedIn: 'root' })
export class DataService {
  constructor(private http: HttpClient) { }

  getData(): Observable<any> {
    return this.http.get<any>('/api/data').pipe(
      catchError(this.handleHttpError)
    );
  }

  private handleHttpError(error: HttpErrorResponse) {
    let errorMessage = 'Unknown error!';
    if (error.error instanceof ErrorEvent) {
      // Client-side errors
      errorMessage = `Error: ${error.error.message}`;
    } else {
      // Server-side errors
      errorMessage = `Error Code: ${error.status}\nMessage: ${error.message}`;
    }
    console.error(errorMessage);
    return throwError(() => new Error(errorMessage));
  }
}

catchError allows you to intercept an error notification, perform some logic (like logging or transforming the error), and then return a new observable, potentially one that emits a default value or re-throws a new error.

3. Centralized HTTP Error Handling with `HttpInterceptor`

For handling HTTP errors consistently across your entire application, HttpInterceptors are invaluable. An interceptor can catch all outgoing HTTP requests and incoming HTTP responses, allowing you to centralize logic for error handling, authentication, logging, etc.

typescript
import { Injectable } from '@angular/core';
import {
  HttpRequest, HttpHandler, HttpEvent, HttpInterceptor, HttpErrorResponse
} from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';

@Injectable()
export class ErrorInterceptor implements HttpInterceptor {
  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    return next.handle(request).pipe(
      catchError((error: HttpErrorResponse) => {
        let errorMessage = '';
        if (error.error instanceof ErrorEvent) {
          // Client-side error
          errorMessage = `Error: ${error.error.message}`;
        } else {
          // Server-side error
          errorMessage = `Error Code: ${error.status}\nMessage: ${error.message}`;
        }
        console.error('HTTP Error via Interceptor:', errorMessage);
        // You can also display a toast or redirect based on error status
        return throwError(() => new Error(errorMessage));
      })
    );
  }
}

To register the interceptor, add it to your AppModule providers:

typescript
import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { ErrorInterceptor } from './error.interceptor';

@NgModule({
  providers: [
    { provide: HTTP_INTERCEPTORS, useClass: ErrorInterceptor, multi: true }
  ]
})
export class AppModule { }

Best Practices for Angular Error Handling

  • Log Errors Effectively: Ensure all errors, especially those caught globally, are logged to a robust monitoring system (e.g., Sentry, New Relic, custom backend).
  • Provide User Feedback: Display user-friendly messages for errors. Avoid showing raw technical error details to end-users.
  • Differentiate Error Types: Handle client-side (network issues, syntax errors) and server-side (API issues, database errors) errors differently.
  • Centralize Where Possible: Use ErrorHandler for application-wide uncaught exceptions and HttpInterceptor for global HTTP errors to avoid repetition.
  • Graceful Degradation/Recovery: Implement retry mechanisms (e.g., retry, retryWhen RxJS operators) for transient network issues, or provide fallback data.
  • Component-Specific Handling: For specific scenarios where a global approach isn't sufficient, handle errors within the component or service using catchError locally.
  • Test Error Paths: Thoroughly test how your application behaves when errors occur to ensure robustness.