banjocode Using Async/Await Within an Angular Interceptor

Using Async/Await Within an Angular Interceptor

Async/Await is great, it makes the code much more readable and clean. It is, however, quite tricky to use it within an Angular interceptor. This is how you do it.

5 min read

Angular interceptors with async/await

I like working with promises, and interceptors (and Angular) usually use observables. Async/await also makes promises so much easier to read and maintain, so that’s why I prefer working with it. It’s, however, a bit complicated to set up and work with, this is one way you can accomplish it.

Scroll to the bottom for a TL;DR.

Full example

Like most other Angular function, we will use an RxJS operator called from, which turns a promise into an observable.

Let’s say this is our base interceptor.

import { HttpInterceptor } from "@angular/common/http";
import { Injectable } from "@angular/core";

@Injectable()
export class ExampleInterceptor implements HttpInterceptor {
	intercept(
		request: HttpRequest<any>,
		next: HttpHandler
	): Observable<HttpEvent<any>> {
		// ...
	}
}

In this example, we will try to catch a 401 error, and handle that asynchronously with async/await. For this, we will also use the catchError operator from RxJS.

return next.handle(request).pipe(
	catchError((error) => {
		if (error instanceof HttpErrorResponse && error.status === 401) {
			// ...
		}
	})
);

The most important logic, the one which lets us use promises. We need to create a new asynchronous method and call it with the from operator.

from(this.handle401Error(request, next));

The function might look something like this.

private async handle401Error(request: HttpRequest<any>, next: HttpHandler) {
    const newRequest = await this.someService.asyncChange(request);
    return next.handle(newRequest).toPromise();
}

We need to use the toPromise() function to turn it into a promise, which the from operator will turn back to an observable.

This is the final code.

import { HttpInterceptor} from '@angular/common/http';
import { Injectable } from '@angular/core';

@Injectable()
export class ExampleInterceptor implements HttpInterceptor {
   intercept(request: HttpRequest<any>, next: HttpHandler):   Observable<HttpEvent<any>> {

        return next.handle(request).pipe(catchError(error => {

            if (error instanceof HttpErrorResponse && error.status === 401) {
                from(this.handle401Error(request, next));
            }
        }))
   }

    private async handle401Error(request: HttpRequest<any>, next: HttpHandler) {
        const newRequest = await await this.someService.asyncChange(request);
        return next.handle(newRequest).toPromise();
    }
}

TL;DR

The code above might look a bit cluttered. The most important parts are these two:

if (error instanceof HttpErrorResponse && error.status === 401) {
	from(this.handle401Error(request, next));
}
private async handle401Error(request: HttpRequest<any>, next: HttpHandler) {
    const newRequest = await await this.someService.asyncChange(request);
    return next.handle(newRequest).toPromise();
}