Thought Leadership
Mar 6, 2023

Functional router guards in Angular 15 open the door to happier code

How to seamlessly migrate deprecated class guards into functional ones

Functional router guards in Angular 15 open the door to happier code

The class-based implementation of guards has been marked as @deprecated in Angular documentation.

Functional router guards replace the class-based approach.

Mark from the Angular team notes three highlights about this feature:

  1. You can now write guards and resolvers as JavaScript functions
  2. Less boilerplate, more flexibility
  3. Inject dependencies

I designed a demo Angular application using version 15 that includes a small number of routes to allow for easy navigation between pages.

Come explore in both main branch on GitHub: https://github.com/katesky/functional-router-guards

as well as in the new implementation branch: https://github.com/katesky/functional-router-guards/tree/convert-to-inject

https://github.com/katesky/functional-router-guards/tree/convert-to-inline-function

The Demo

The Account page is protected by a route guard which only allows access if the canActivate function returns true. If a user attempts to access the Account page without being authenticated, they will be redirected to the Login page.

The Login page includes a login button that allows users to enter their credentials and authenticate. Upon successful authentication, users are redirected back to the Account page.

Users who are already authenticated and are on the Account page can log out by clicking the logout button. This will reset the login status and require users to authenticate again if they attempt to access the Account page.

Advantages of Functional Guards

  1. Reduced Boilerplate Code: Functional router guards don’t require a separate guard class definition. This can make your code more concise and easier to read.
  2. Simplified Logic: Functions are often easier to understand and reason about than classes, since they don’t rely on object-oriented concepts like inheritance and encapsulation. This can make it easier to write and maintain complex guard logic.
  3. Increased Flexibility: Functional router guards can be defined inline, which means that you can easily modify or customize the guard logic on a per-route basis. This can be especially useful if you have a variety of routes with different guard requirements.
  4. Improved Performance: Functional router guards are often more performant than class-based guards, since they don’t require the overhead of creating and instantiating a separate class instance.
  5. Better Type Inference: Functional router guards are often easier to type correctly than class-based guards, since TypeScript can infer the types of the function parameters and return values automatically. This can help catch type errors early in the development process.

Implementation

This is how we define an AuthGuardservice that checks whether a user is logged in before allowing access to a guarded route, and redirects unauthenticated users to the /login page:

We then define an array of routes that can be used in an Angular application in the AppRoutingModule. The canActivate property is used to specify the AuthGuardservice as a route guard for the /account path. This ensures that the/account route can only be accessed if the user is authenticated.

Let make sure our code works (view StackBlitz here).

Functional Approach

When developing a new codebase, it is advisable to avoid the traditional class-based implementation of guards and instead define them as functions. This approach can result in a more efficient and streamlined implementation while still fulfilling the intended functionality of guards in Angular applications.

In order to transform a class-based guard into a functional route guard, we can utilize the inject function from @angular/core, which provides dependency injection within a functional context and has been available since Angular v14.

By leveraging this functionality, we can create a reusable and composable function that can be directly passed to the Angular Router’s canActivateand canActivateChild functions as well as other router guards. Using a functional approach also ensures that functions can be correctly typed, thereby reducing the likelihood of type errors.

Here we define a reusable route guard function that can be used directly in the canActivateand canActivateChild properties of an Angular route definition.

TheauthGuard function will check whether the user is authenticated before allowing access to the AccountComponent in the AppRoutingModule:

We can also implement AccountComponent route canActivate guard using in-line style function like this:

Let's make sure our code still runs as before (click here to view StackBlitz).

Summary

Functional router guards are more streamlined and efficient, allowing for a cleaner implementation in modern Angular applications. They are easier to compose, reuse, and define complex route guard logic. They use Angular’s dependency injection system, which provides greater flexibility and extensibility compared to class-based guards.

Furthermore, functional router guards can be correctly typed, reducing the likelihood of type errors and improving the overall maintainability of the codebase.

Here are those links to my demo app one more time:

Main GitHub Branch: https://github.com/katesky/functional-router-guards

Implementation Branch: https://github.com/katesky/functional-router-guards/tree/convert-to-inject

. . .
Article Summary
Discover how to migrate from class-based to functional router guards in Angular 15, enhancing code efficiency, flexibility, and performance.
Author
Kate Gable
Angular Consultant
Related Articles
Open Source Insights Delivered Monthly

By clicking “submit” I acknowledge receipt of our Privacy Policy.

Thanks for signing up for our Newsletter! We look forward to connecting with you.
Oops! Something went wrong while submitting the form.