Global error handling with Angular and NGRX

Before reading make sure to open the demo app on Stackblitz and try clicking on the Cause Error button. The message you see is handled via the HttpInterceptor.

Every web app needs error handling. How the error is handled can be very specific, and the UX around error handling can vary a lot. One could offer a user to retry their action if the error occured, or open up a whole new workflow if that would help the user to solve his error and make another attempt. But the minimum would be to display the error to the user, so he knows the error occured, his original intent has most likely failed and to give him some description of the error.

What I want to show here is how to solve error display on a global level, so that each request which returns an error is displayed to the user, without the need to address every http request individually.

This demo is built with Angular, and I have added NGRX, which is completely optional, it can be replaced by a simple custom service.

The demo app is simple. You will see a button which on click makes a http request on a non existing address, causing the error. The error is in turn displayed.

The basic flow depicts how the error is handled, and here’s the explanation.

  1. Http request is made and the error occurs
  2. HttpInterceptor catches the error, and dispatches a NGRX action with the error, and rethrows the error -> this is crucial if you want to additionally catch the error in the subscribe
  3. The error is added to the NGRX Store
  4. App Component listens to the Store and displays an error once the error becomes different than null

After we get our hands dirty with few lines of code I’ll explain few simple things you can do to improve this UX wise.

HttpInterceptor

The crucial building block here is the HttpInterceptor -> and this one is simple. It catches each request and catches the error responses. Which are then dispatched as a payload in the NGRX action, and the error is rethrown.

@Injectable()
export class ErrorInterceptor implements HttpInterceptor {

constructor(
private store: Store<DemoState>
) {}

intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next
.handle(request)
.pipe(
catchError((error: HttpErrorResponse) => {
this.store.dispatch(new AddGlobalError(error));
return throwError(error);
})
)
}
}

Make sure not to forget to add the HttpInterceptor as a provider in the module providers:

providers: [
{ provide: HTTP_INTERCEPTORS, useClass: ErrorInterceptor, multi: true}
],

NGRX Stack

The situation here is pretty simple. We just have a single property of the state, called error, where we save the most recent http error once it occurs.

The Action

export class AddGlobalError implements Action {
readonly type = ADD_GLOBAL_ERROR;
constructor(public payload: any) {}
}

The Reducer

export function reducer(state: any = null, action: Actions.All) {
switch (action.type) {

case Actions.ADD_GLOBAL_ERROR: {
return action.payload;
}

default:
return state;
}
}

App Component

In the app component we’re listening to the store and we have an observable with the error selector.

export class AppComponent {

error$: Observable<any>;

constructor(
private store: Store<DemoState>,
private http: HttpClient
) {
this.error$ = store.pipe(select('error'));
}

causeError() {
this.http.get('http://www.zhgdsfdsfsdf.com').subscribe();
}
}

And in the template we have a button which calls the causeError() method, which in turn makes a faulty http request.

<h1>Global Error Demo</h1>
<p>Click the button to make a request which will result in an error.</p>
<button (click)="causeError()">Cause error</button>
<div *ngIf="error$ | async as error" class="error">{{error.message}}</div>

The request fails and the previously explained interceptor catches the error and passes it along the NGRX action, which the reducer saves in the state.

Make sure to check out the full codebase for the example on Stackblitz — Global Error Demo.


In the example the error display and template are very simple. What you can do here is implement this to work with Angular Material Snackbar or some other Toast like component.

Also you can very easily save all intercepted errors in the Store and have them ready to be displayed in some sort of a Error Log display in your application.

This approach is nice if you want to have errors displayed to the user, but you don’t require each error to be handled individually. I recommend using this as soon as you start the application, and build up the UX as you go forward and specific situations occur.

You can easily exclude specific http requests from the interceptor and handle them on the subscribe level, or if you’re using NGRX Effects do it on the effect level.


If you have some questions on how to use this in more complex environments and in your specific business case feel free to send me a message.