Route Guards
Controlling whether a route is allowed to activate, most commonly to protect pages behind authentication.
読了時間 3 分
Some routes shouldn't be reachable unconditionally — a settings page that requires a logged-in user, an admin panel that requires a specific role, a multi-step form that shouldn't be entered halfway through. A route guard is a function the router checks before letting navigation to a route complete.
A CanActivate guard
import { inject } from '@angular/core';
import { CanActivateFn, Router } from '@angular/router';
import { AuthService } from './auth.service';
export const authGuard: CanActivateFn = () => {
const auth = inject(AuthService);
const router = inject(Router);
if (auth.isLoggedIn()) {
return true;
}
router.navigate(['/login']);
return false;
};A CanActivateFn is a plain function — modern Angular guards don't need to be classes — that returns (or resolves to) true to allow navigation, or false to block it. Here, an unauthenticated user is redirected to /login and the original navigation is cancelled. Because it's a function, inject() works exactly as it would inside a component or service, giving the guard access to whatever services it needs to make its decision.
Applying it to a route
// app.routes.ts
import { Routes } from '@angular/router';
import { authGuard } from './auth.guard';
import { SettingsComponent } from './settings/settings.component';
export const routes: Routes = [
{ path: 'settings', component: SettingsComponent, canActivate: [authGuard] },
];canActivate takes an array, so multiple guards can be attached to one route — all of them must return true for navigation to proceed. Angular checks them before the component is even created, so an unauthorized user's browser never instantiates SettingsComponent or fetches whatever data it would normally load.
Guards that return an observable
A guard doesn't have to answer synchronously. If the check requires an async call — verifying a session with the server, say — a guard can return an observable (or a promise) instead of a plain boolean, and the router waits for it to resolve:
import { inject } from '@angular/core';
import { CanActivateFn } from '@angular/router';
import { map } from 'rxjs/operators';
import { AuthService } from './auth.service';
export const verifiedSessionGuard: CanActivateFn = () => {
const auth = inject(AuthService);
return auth.checkSession().pipe(
map((isValid) => isValid),
);
};Other guard types
CanActivate is the one you'll reach for most, but the router supports a few related guard types for other moments in navigation: CanDeactivate runs before leaving a route — useful for warning a user about unsaved changes in a form — and CanMatch decides whether a route should even be considered a match in the first place, which is handy for choosing between two different components for the same path based on some condition (like a feature flag).
A guard isn't a substitute for server-side checks
A route guard controls what the Angular app displays — it stops a component from rendering, and stops the client from making requests it shouldn't. It runs entirely in the user's browser, and someone could disable JavaScript protections or call an API directly. Any real security boundary — deciding who's allowed to read or modify data — has to be enforced by the backend as well; guards are about user experience and preventing accidental access, not a replacement for server-side authorization.