A New Way Of Ordering Guards In Angular
Tags: angular, routing
Published: 2022-12-15 14:25:49

## Overview

With the new Angular feature "functional guards and resolvers", it's easier to make sure that your route guards execute in order instead of all at once.

When there are multiple guards in a route configuration, they are normally executed all at the same time. For example, let's say you have the following route configuration:

```ts
{
    path: 'crisis-center',
    canActivate: [FirstGuard, SecondGuard],
    ...
}
```

Even though the guards are in an array, `SecondGuard` will always execute no matter whether `FirstGuard` returns `true` or not. What we would like to achieve instead is to have `SecondGuard` not fire at all if `FirstGuard` doesn't allow it.

For a more life-like example, imagine that your first guard makes sure the user is properly logged in, while the second guard already assumes that and proceeds to call an API that works only for logged-in users.

## Functional solution

Up to recent times, the best way to achieve guard ordering was to write an extra "parent" guard to call the other guards in order. This meant that you had to create an additional ordering guard class for every configuration of ordered guards, or to write your guard in a more generic way that made use of the router's `data` object.

Now there's a much simpler way. If you search for "Functional router guards" in the [Angular 15 announcement blog post](https://blog.angular.io/angular-v15-is-now-available-df7be7f2f4c8), you'll see a short description of a new Angular feature. What it says is that you can now use functions instead of classes wherever you want to put a route guard.

In the simplest form, it means we can write the following in the route configuration:

```ts
   canActivate: [() => isUserLoggedIn()]
```



If we combine this new feature with the recently-updated `inject` function, we could replace any guard class as follows:

```ts
  canActivate: [FirstGuard]
```

with:

```ts
  canActivate: [(route, state) =>
    inject(FirstGuard).canActivate(route, state)]
```

While this is more verbose, it gives us a lot of power to, e.g., combine guards.

## Ordered synchronous guards

For the first case, imagine that all your guards are synchronous and they immediately return a boolean. If you want to make them execute in order, you could write the following:

```ts
const orderedSyncGuards =
  (guards) =>
    (route, state) =>
      guards.every(guard =>
        inject(guard).canActivate(route, state));

const ROUTE = {
  ...
  canActivate: [orderedSyncGuards([FirstGuard, SecondGuard])]
```

This example is a bit simplistic, and I removed types of `orderedSyncGuards` to make the code more readable. Let's now go for a full-blown example with asynchronous guards that can also return `UrlTrees`.

## Ordered asynchronous guards

However, before I go there, a short disclaimer: executing multiple async guards in order **will make your page load slower** because later guards won't even start their requests until earlier guards finish. If you value performance, you are better off rewriting your guards to handle unfavorable conditions instead. The solution below is provided for the sake of completeness.

You can [follow the full example on StackBlitz](https://stackblitz.com/edit/angular-y5slak?file=src/app/app-routing.module.ts).

This time we'll assume that all guards return observables that emit either a boolean or a `UrlTree` and then complete.

Because we want to run the guards one after another, we can use `concatMap` as our main operator. We also want to break out of the function if the value returned by any guard is different than `true`, and we want to return the last emitted value.

After some fun with TypeScript types, the following is the final outcome:

```ts
interface AsyncGuard extends CanActivate {
  canActivate(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
  ): Observable<boolean | UrlTree>;
}

function orderedAsyncGuards(
  guards: Array<new () => AsyncGuard>
): CanActivateFn {
  return (route, state) => {
    // Instantiate all guards.
    const guardInstances = guards.map(inject) as AsyncGuard[];
    // Convert an array into an observable.
    return from(guardInstances).pipe(
      // For each guard, fire canActivate and wait for it
      // to complete.
      concatMap((guard) => guard.canActivate(route, state)),
      // Don't execute the next guard if the current guard's
      // result is not true.
      takeWhile((value) => value === true, /* inclusive */ true),
      // Return the last guard's result.
      last()
    );
  };
}

const ROUTE = {
  ...
  canActivate: [orderedAsyncGuards([FirstGuard, SecondGuard])]
```

And that's how you can write a universal sync or async guard ordering, using a single function call!

I will leave writing a universal ordering guard (sync/async, boolean/UrlTree) as an exercise for the reader :) Here's a hint: start with the async solution and inspect the result of `guard.canActivate` call.