Application diagnostics
Angular SPA Typescript Visual Studio Code

How to Implement Centralized Error Handling in Angular Applications

Welcome to today’s blog.

In today’s blog I will discuss how to configure and implement centralized error-handling in Angular applications.

In a previous post I showed how to handle errors that are generated from executing HTTP requests. This was localized error-handling from a service. We would then handle the error within the calling Angular component or service.

In this post, before I show how to implement a global error handler within an Angular application, I will explain a common error handling class that we frequently use. I will then show an example of how errors are handled using the traditional approach.

The ErrorHandler Class

With a centralized error handler, we can handle the error within a shared, or common class within the application. By refactoring the error handling, we can promote re-use of the application error handling and promote better code re-use, making the error-handling unit testable.

To create a derived custom error class, we can override the ErrorHandler core class, which is documented in the Angular developer site.

The ErrorHandler class provides a hook for centralized error handling. The default behavior outputs messages to the console. In our application, we can override the default handler method handleError(error) to provide a custom error message or logging suitable for our application requirements.    

Traditional Approach Localized within a Method

I will show how this is done within a common scenario of the context of an error response piped from an HTTP API call.  

First, take our call to the HTTP GET API method shown below:

getBooks(): Observable<Book[]> {
    return this.http.get<Book[]>(this.config.baseApiUrl + '/Book/List')
        .pipe(
            catchError(err => this.handleError(err)),
                finalize(() => {
                    console.log("completed call to getBook()")
                }
            )
        ); 
}

When we pipe the error from the HTTP request through the catchError() method, the handling is local to the component or service that contains the getBooks() method.

Within the local handler method, we analyze the error status and determine the type of output to the console.

private handleError(error: HttpErrorResponse) {
    if (error.status === 0) {
        console.error('An error has occurred:', error.error);
    } else {
        console.error(
            `Backend has returned code ${error.status}, 
            the body was: `, error.error);
    }
    return throwError(
        'An error occurred; please re-try the request.'
    );
}

If we could could move the above local error handler into a shared class, it would achieve our goal of centralized error handling. However, this would not amend the default behavior of the built-in Angular error handler. 

In the next section, I will show how to derive a global error handler from the standard error handler class.

Deriving a Global Error Handler from ErrorHandler

Recall that I mentioned that we can override the default error handler ErrorHandler. We do this as shown:

import { ErrorHandler } from "@angular/core";

export class GlobalErrorHandler implements ErrorHandler {
    handleError(error) {
        console.error(
            `Global error :  ${error}`, error);
        }
    }

For the overridden error handler to replace the default error handler we will need to add the new global error handler as a provider within NgModule. This is done as follows:

import { GlobalErrorHandler } from './utilities/globalErrorHandler';

@NgModule({
    declarations: [
      …
    ],
    imports: [
      …
    ],
    providers: [
      …
      {
          provide: ErrorHandler, 
          useClass: GlobalErrorHandler
      },
      …
    ],
    bootstrap: [AppComponent]
})

export class AppModule { }

What this does is run the overridden handleError() method of ErrorHandler when an error is thrown.

Recall that our local error handler threw an error message as shown:

return throwError(
    'An error occurred; please re-try the request.'
);

In the next section, I will show how to test the global error handler.

Testing the Global Error Handler

Before we run our application, place two breakpoints,

  1. A breakpoint on the catchError() within the subscribed observable in the API method call, and
  2. Place another breakpoint within the global error handler’s handleError() method.

After we run the application in debug mode, the error trapped within the observable does not throw an error as it was trapped from an error that occurred within the server API method. The inline debugger will continue into the local error handler until it reaches the throwError() command.

Then the next breakpoint will be hit within the global error handler as shown:

You will notice the message we passed within the throwError() method’s parameter will be passed as a parameter in the handleError() method within the global handler.

The resulting console output will show output first from the local error handler, then subsequently from the global error handler as shown.

Ideally, we could customize the error message passed into the throwError() method depending on the status code of the local error object instance and not output any local error messages to the console.  

By configuring and implementing the above global error handler, we have shown how useful it can be to make our error handling logic tidier and unit testable.

Details on the Angular core library ErrorHandler class are in the Angular developer site.

That is all for today’s post.

I hope you found this post useful and informative.

Social media & sharing icons powered by UltimatelySocial