Migrating from Angular Class-Based Interceptors to Function-Based Interceptors:
In this article, we'll explore how to migrate from class-based interceptors to function-based interceptors. With the release of Angular v15, the Angular team introduced a new way to create interceptors. Instead of creating a class that implements the HttpInterceptor interface, we can now create a function that implements the HttpInterceptorFn interface.
Why should we migrate to function-based interceptors?
Class-based interceptors are great, but they might be deprecated in a later version. An indicator of this is for example: withInterceptorsFromDi() 🤔
In addition to this, function-based interceptors are better than class-based interceptors for several reasons:
Easier usage
Quick creation
Simple configuration
Migration of the AuthorizationInterceptor to a function-based interceptor
The interceptor currently looks like this:
1import { Injectable, inject } from '@angular/core';
2import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor } from '@angular/common/http';
3import { Observable, mergeMap, tap } from 'rxjs';
4import { AngularFireAuth } from '@angular/fire/compat/auth';
5
6@Injectable()
7export class AuthorizationInterceptor implements HttpInterceptor {
8 readonly angularFireAuth = inject(AngularFireAuth);
9
10 intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
11 if (request.url.indexOf('oauthCallback') > -1) {
12 return next.handle(request);
13 }
14 return this.angularFireAuth.idToken.pipe(
15 mergeMap((token) => {
16 if (token) {
17 request = request.clone({
18 setHeaders: {
19 Authorization: `Bearer ${token}`,
20 },
21 });
22 }
23 return next.handle(request);
24 }),
25 );
26 }
27}
Creating and using a function-based interceptor
To better understand the migration process, we'll first create a simple function-based interceptor:
1import { HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
2
3export const simpleInterceptorFn: HttpInterceptor = (req: HttpRequest<any>, next: HttpHandler) => {
4 return next.handle(req);
5};
And we can use it as follows:
1@NgModule({
2 providers: [
3 provideHttpClient(
4 withInterceptors([simpleInterceptorFn])
5 ),
6 ],
7})
8export class AppModule {}
or in a standalone application:
1bootstrapApplication(AppComponent, {
2 providers: [
3 provideHttpClient(withInterceptors([simpleInterceptorFn]))
4 ]
5});
That's it! We have created a simple function-based interceptor that we can use in our application.
Migration of the AuthorizationInterceptor to a function-based interceptor
Now that we know how to create and use a function-based interceptor, let's migrate the AuthorizationInterceptor to a function-based interceptor.
Since our interceptor depends on other services, we need to inject these using the inject function.
Here is the updated version of the AuthorizationInterceptor as a function-based interceptor:
1export function authorizationInterceptor(request: HttpRequest<unknown>, next: HttpHandlerFn) {
2 const angularFireAuth = inject(AngularFireAuth);
3
4 if (request.url.indexOf('oauthCallback') > -1) {
5 return next(request);
6 }
7
8 return angularFireAuth.idToken.pipe(
9 mergeMap((token) => {
10 if (token) {
11 request = request.clone({
12 setHeaders: {
13 Authorization: `Bearer ${token}`,
14 },
15 });
16 }
17 return next(request);
18 }),
19 );
20}
We can now use it in the following way:
1@NgModule({
2 providers: [
3 provideHttpClient(withInterceptors([authorizationInterceptor])),
4 ],
5})
6export class AppModule {}
or in a standalone application:
1
2bootstrapApplication(AppComponent, {
3 providers: [
4 provideHttpClient(withInterceptors([authorizationInterceptor]))
5 ]
6});
That's it!
Before vs. After 🚀
| | |
| ------ | ------ |
| |
|
Conclusion
The migration from class-based interceptors to function-based interceptors provides numerous benefits. It simplifies the code, makes it more flexible and more maintainable. If your interceptors depend on other services, you can effortlessly use these in the functions creating the interceptors. This provides a clear separation of responsibilities and fosters better testability.
It is recommended to consider the transition to function-based interceptors early in order to benefit from the numerous advantages and to be prepared for future Angular versions.