Angular Functional Guards - How to Use Functional Router Guards

In this post you will learn how to create functional guards inside Angular.

I already made a post How to make guards in Angular using classes. This is exactly what we used in Angular previously. But starting from Angular 15 classes approach is deprecated and obviously is not recommended.

Guards

Now we have a new functional approach. And actually I think that it changes quite a lot of things inside Angular. This is a first huge step to make Angular more functional. Previously we wrote everything like classes but now we start to write things functionally with the approach this is similar to React for example.

So the goal of this post is to take existing class guards and refactor it to a function.

What we have

Here I already prepared a project with Angular 15.

Starting project

We have 2 routes Home and Private. In order to access private route we must have a token inside localStorage which is checked by our guard. Here is how it looks like.

@Injectable()
export class AuthGuardService implements CanActivate {
  constructor(
    private currentUserService: CurrentUserService,
    private router: Router
  ) {}

  canActivate(): Observable<boolean> {
    return this.currentUserService.currentUser$.pipe(
      filter((currentUser) => currentUser !== undefined),
      map((currentUser) => {
        if (!currentUser) {
          this.router.navigateByUrl('/');
          return false;
        }
        return true;
      })
    );
  }
}

Here we injected currentUserService which helps us to get currentUser$ and check if we logged in or not. As getting of current user is an asynchronous process we must filter undefined value which is our initial value. After checking the current user we either get an object or null.

Rewrite

Now let's try to rewrite our class in functional style. In order to do that I want to create a new file auth.guard.ts.

export const authGuard = () => {
  return this.currentUserService.currentUser$.pipe(
    filter((currentUser) => currentUser !== undefined),
    map((currentUser) => {
      if (!currentUser) {
        this.router.navigateByUrl('/');
        return false;
      }
      return true;
    })
  );
}

This code won't work as I just created a function and copied the content of canActivate inside. The main problem is that we use here currentUserService and router which both need to be injected.

In order to write that in functional way we are using inject function.

export const authGuard = () => {
  const currentUserService = inject(CurrentUserService);
  const router = inject(Router);

  return currentUserService.currentUser$.pipe(
    filter((currentUser) => currentUser !== undefined),
    map((currentUser) => {
      if (!currentUser) {
        router.navigateByUrl('/');
        return false;
      }
      return true;
    })
  );
};

As you can see we injected first CurrentUserService and Router as local properties.

The last thing that we are missing is to change the usage of our guard from class to a function.

import { authGuard } from '../auth.guard';

const routes: Routes = [
  {
    path: 'private',
    component: PrivateComponent,
    canActivate: [authGuard],
  },
];

Here in canActivate instead of the class we are using functional guard.

Starting project

As you can see in browser it works exactly the same as before.

Want to conquer your next JavaScript interview? Download my FREE PDF - Pass Your JS Interview with Confidence and start preparing for success today!

📚 Source code of what we've done
Did you like my post? Share it with friends!
Don't miss a thing!
Follow me on Youtube, Twitter or Instagram.
Oleksandr Kocherhin
Oleksandr Kocherhin is a full-stack developer with a passion for learning and sharing knowledge on Monsterlessons Academy and on his YouTube channel. With around 15 years of programming experience and nearly 9 years of teaching, he has a deep understanding of both disciplines. He believes in learning by doing, a philosophy that is reflected in every course he teaches. He loves exploring new web and mobile technologies, and his courses are designed to give students an edge in the fast-moving tech industry.