Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Vue Router 4 reference covering navigation guards, route params, lifecycle interactions, and common gotchas.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
reference/router-guard-async-await-pattern.md
1---2title: Async Navigation Guards Require Proper Promise Handling3impact: MEDIUM4impactDescription: Unawaited promises in guards cause navigation to complete before async checks finish, allowing unauthorized access or missing data5type: gotcha6tags: [vue3, vue-router, navigation-guards, async, promises]7---89# Async Navigation Guards Require Proper Promise Handling1011**Impact: MEDIUM** - Navigation guards that perform async operations (API calls, auth checks) must properly handle promises. If you don't await async operations or return the promise, navigation completes before your check finishes, potentially allowing unauthorized access or navigating with incomplete data.1213## Task Checklist1415- [ ] Use async/await in navigation guards16- [ ] Return the promise if not using async/await17- [ ] Add loading states for long async operations18- [ ] Implement timeouts for slow API calls19- [ ] Handle errors to prevent navigation hanging2021## The Problem2223```javascript24// WRONG: Not awaiting - navigation proceeds immediately25router.beforeEach((to, from) => {26if (to.meta.requiresAuth) {27checkAuth() // This returns a Promise but we're not waiting!28// Navigation continues before checkAuth completes29}30})3132// WRONG: Async function but forgot return33router.beforeEach(async (to, from) => {34if (to.meta.requiresAuth) {35const isValid = await checkAuth()36if (!isValid) {37// This redirect might happen after navigation already completed!38return '/login'39}40}41// Missing return - implicitly returns undefined, allowing navigation42})43```4445## Solution: Proper Async/Await Pattern4647```javascript48// CORRECT: Async function with proper returns49router.beforeEach(async (to, from) => {50if (to.meta.requiresAuth) {51try {52const isAuthenticated = await checkAuth()5354if (!isAuthenticated) {55return { name: 'Login', query: { redirect: to.fullPath } }56}57} catch (error) {58console.error('Auth check failed:', error)59return { name: 'Error', params: { message: 'Authentication failed' } }60}61}62// Explicitly return nothing to proceed63return true64})65```6667## Solution: Promise-Based Pattern (Alternative)6869```javascript70// CORRECT: Return promise explicitly71router.beforeEach((to, from) => {72if (to.meta.requiresAuth) {73return checkAuth()74.then(isAuthenticated => {75if (!isAuthenticated) {76return { name: 'Login' }77}78})79.catch(error => {80console.error('Auth check failed:', error)81return { name: 'Error' }82})83}84})85```8687## Loading State During Async Guards8889```javascript90// app/composables/useNavigationLoading.js91import { ref } from 'vue'9293const isNavigating = ref(false)9495export function useNavigationLoading() {96return { isNavigating }97}9899export function setupNavigationLoading(router) {100router.beforeEach(() => {101isNavigating.value = true102})103104router.afterEach(() => {105isNavigating.value = false106})107108router.onError(() => {109isNavigating.value = false110})111}112```113114```vue115<!-- App.vue -->116<script setup>117import { useNavigationLoading } from '@/composables/useNavigationLoading'118119const { isNavigating } = useNavigationLoading()120</script>121122<template>123<LoadingBar v-if="isNavigating" />124<router-view />125</template>126```127128## Timeout Pattern for Slow APIs129130```javascript131// CORRECT: Add timeout to prevent indefinite waiting132function withTimeout(promise, ms = 5000) {133return Promise.race([134promise,135new Promise((_, reject) =>136setTimeout(() => reject(new Error('Request timeout')), ms)137)138])139}140141router.beforeEach(async (to, from) => {142if (to.meta.requiresAuth) {143try {144const isValid = await withTimeout(checkAuth(), 5000)145if (!isValid) {146return '/login'147}148} catch (error) {149if (error.message === 'Request timeout') {150// Let user through but show warning151console.warn('Auth check timed out')152} else {153return '/login'154}155}156}157})158```159160## Multiple Async Checks161162```javascript163// CORRECT: Run independent checks in parallel164router.beforeEach(async (to, from) => {165if (to.meta.requiresAuth && to.meta.requiresSubscription) {166try {167const [isAuthenticated, hasSubscription] = await Promise.all([168checkAuth(),169checkSubscription()170])171172if (!isAuthenticated) {173return '/login'174}175176if (!hasSubscription) {177return '/subscribe'178}179} catch (error) {180return '/error'181}182}183})184```185186## Error Handling Best Practices187188```javascript189router.beforeEach(async (to, from) => {190try {191// Your async logic here192await performChecks(to)193} catch (error) {194// Always handle errors to prevent navigation from hanging195196if (error.response?.status === 401) {197return '/login'198}199200if (error.response?.status === 403) {201return '/forbidden'202}203204if (error.code === 'NETWORK_ERROR') {205// Offline - maybe allow navigation but show warning206return true207}208209// Unknown error - redirect to error page210console.error('Navigation guard error:', error)211return { name: 'Error', state: { error: error.message } }212}213})214```215216## Key Points2172181. **Always await async operations** - Otherwise navigation proceeds immediately2192. **Return values matter** - Return route to redirect, false to cancel, true/undefined to proceed2203. **Handle all error cases** - Uncaught errors can hang navigation2214. **Add timeouts** - Slow APIs shouldn't block navigation indefinitely2225. **Show loading state** - Users need feedback during async checks2236. **Parallelize independent checks** - Use Promise.all for better performance224225## Reference226- [Vue Router Navigation Guards](https://router.vuejs.org/guide/advanced/navigation-guards.html)227- [Vue Router Navigation Failures](https://router.vuejs.org/guide/advanced/navigation-failures.html)228