Outcome Recipes
Advanced routing patterns for authentication, data preloading, guards, and complex navigation scenarios using @aurelia/router.
1. Global authentication guard for all routes
Steps
import { observable } from '@aurelia/runtime'; export interface User { id: string; name: string; email: string; roles: string[]; } export class AuthService { @observable isAuthenticated = false; currentUser: User | null = null; constructor() { const token = localStorage.getItem('auth_token'); if (token) { this.validateToken(token); } } async login(email: string, password: string): Promise<boolean> { try { const response = await fetch('/api/auth/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email, password }) }); if (!response.ok) { return false; } const { token, user } = await response.json(); localStorage.setItem('auth_token', token); this.currentUser = user; this.isAuthenticated = true; return true; } catch { return false; } } logout() { localStorage.removeItem('auth_token'); this.currentUser = null; this.isAuthenticated = false; } hasRole(role: string): boolean { return this.currentUser?.roles.includes(role) ?? false; } private async validateToken(token: string) { try { const response = await fetch('/api/auth/validate', { headers: { 'Authorization': `Bearer ${token}` } }); if (response.ok) { const user = await response.json(); this.currentUser = user; this.isAuthenticated = true; } else { localStorage.removeItem('auth_token'); } } catch { localStorage.removeItem('auth_token'); } } }import { lifecycleHooks } from '@aurelia/runtime-html'; import { IRouteViewModel, IRouter, Params, RouteNode, NavigationInstruction } from '@aurelia/router'; import { resolve } from '@aurelia/kernel'; import { AuthService } from './auth-service'; @lifecycleHooks() export class GlobalAuthGuard { private auth = resolve(AuthService); private router = resolve(IRouter); // List of routes that don't require authentication private publicRoutes = ['login', 'register', 'forgot-password', '']; canLoad( viewModel: IRouteViewModel, params: Params, next: RouteNode, current: RouteNode | null ): boolean | NavigationInstruction { const routePath = next.route.path; // Check if route is public const isPublicRoute = this.publicRoutes.some(path => routePath === path || routePath?.startsWith(`${path}/`) ); if (isPublicRoute) { return true; } // Check authentication for protected routes if (!this.auth.isAuthenticated) { // Store intended destination sessionStorage.setItem('returnUrl', next.path); // Redirect to login return 'login'; } // Check role-based access if route has role requirements const requiredRole = next.route.data?.requiredRole; if (requiredRole && !this.auth.hasRole(requiredRole)) { return 'unauthorized'; } return true; } }import Aurelia from 'aurelia'; import { RouterConfiguration } from '@aurelia/router'; import { GlobalAuthGuard } from './global-auth-guard'; import { AuthService } from './auth-service'; import { MyApp } from './my-app'; Aurelia .register( RouterConfiguration, AuthService, GlobalAuthGuard // Registered globally - runs for ALL routes ) .app(MyApp) .start();import { route } from '@aurelia/router'; @route({ routes: [ { path: '', component: () => import('./pages/home'), title: 'Home' }, { path: 'login', component: () => import('./pages/login'), title: 'Login' }, { path: 'dashboard', component: () => import('./pages/dashboard'), title: 'Dashboard' // No role needed - just requires authentication }, { path: 'admin', component: () => import('./pages/admin'), title: 'Admin Panel', data: { requiredRole: 'admin' } } ] }) export class MyApp {}import { IRouter } from '@aurelia/router'; import { resolve } from '@aurelia/kernel'; import { AuthService } from './auth-service'; export class Login { private router = resolve(IRouter); private auth = resolve(AuthService); email = ''; password = ''; error = ''; async submit() { const success = await this.auth.login(this.email, this.password); if (success) { // Redirect to intended destination or home const returnUrl = sessionStorage.getItem('returnUrl') || 'dashboard'; sessionStorage.removeItem('returnUrl'); await this.router.load(returnUrl); } else { this.error = 'Invalid credentials'; } } }
Checklist
When to use this approach
2. Data preloading with loading states
Steps
Checklist
3. Preventing navigation with unsaved changes
Steps
Checklist
4. Query parameter state management
Steps
Checklist
Router pattern cheat sheet
Pattern
Key Hook
Use When
Best practices
See also
Last updated
Was this helpful?