Hone logo
Hone
Problems

Vue Router Middleware Implementation

This challenge focuses on implementing a common pattern in modern web applications: middleware for route protection and manipulation in Vue. You will build a system that allows you to define and apply functions to specific routes, influencing navigation behavior before a component is rendered. This is crucial for tasks like authentication, authorization, and data prefetching.

Problem Description

Your task is to implement a system for handling middleware in a Vue 3 application using Vue Router. You need to create a way to define middleware functions and associate them with specific routes. These middleware functions should execute sequentially before a route is navigated to, and they should have the ability to:

  1. Perform actions: This could include checking user authentication, validating permissions, or fetching data required by the route.
  2. Control navigation: Middleware can decide whether to allow the navigation to proceed, redirect the user to a different route, or even abort the navigation entirely.

You will need to integrate this middleware system with Vue Router's navigation guards.

Key Requirements:

  • Define a mechanism to register middleware functions.
  • Associate middleware functions with individual routes.
  • Ensure middleware functions execute in the order they are defined for a given route.
  • Allow middleware functions to access route information (current route, future route, router instance).
  • Enable middleware functions to control navigation flow (e.g., router.push, router.replace, or aborting navigation).
  • Handle cases where no middleware is defined for a route.
  • Integrate this system with Vue Router's beforeEach navigation guard.

Expected Behavior:

When a user attempts to navigate to a route:

  • The router should identify all middleware functions associated with that route.
  • These middleware functions should execute in the order they were defined.
  • If any middleware function redirects the user or aborts the navigation, subsequent middleware and the actual route transition should be halted.
  • If all middleware functions complete successfully, the navigation to the target route should proceed.

Edge Cases:

  • Routes with no middleware defined.
  • Middleware functions that throw errors.
  • Nested routes and how middleware applies to them (for this challenge, assume middleware is applied directly to leaf routes or parent routes that are directly navigated to).
  • Middleware that causes an infinite redirect loop.

Examples

Example 1: Authentication Middleware

Consider a route /dashboard that requires the user to be logged in.

// Assume router instance is created and configured

// Define a simple authentication middleware
const requiresAuth = (to: RouteLocationNormalized, from: RouteLocationNormalized, next: NavigationGuardNext) => {
  const isAuthenticated = localStorage.getItem('authToken'); // Dummy check
  if (isAuthenticated) {
    next(); // Allow navigation
  } else {
    next('/login'); // Redirect to login page
  }
};

// Associate middleware with a route
const routes: Array<RouteRecordRaw> = [
  {
    path: '/login',
    component: LoginView,
  },
  {
    path: '/dashboard',
    component: DashboardView,
    meta: {
      middleware: [requiresAuth], // Apply middleware here
    },
  },
];

// In your router setup, you would iterate through routes and attach beforeEach guards.
// The system you build should facilitate this.

Explanation:

When navigating to /dashboard, the requiresAuth middleware is executed. If an authToken exists in localStorage, navigation proceeds. Otherwise, the user is redirected to /login.

Example 2: Role-Based Access Control

A route /admin requires the user to have an 'admin' role.

// Assume router instance and user store are set up

const requiresAdminRole = (to: RouteLocationNormalized, from: RouteLocationNormalized, next: NavigationGuardNext) => {
  const userRole = getUserRole(); // Function to get current user's role from a store or API
  if (userRole === 'admin') {
    next();
  } else {
    next('/forbidden'); // Redirect to a forbidden page
  }
};

const routes: Array<RouteRecordRaw> = [
  // ... other routes
  {
    path: '/admin',
    component: AdminView,
    meta: {
      middleware: [requiresAdminRole],
    },
  },
  {
    path: '/forbidden',
    component: ForbiddenView,
  },
];

Explanation:

Navigating to /admin triggers requiresAdminRole. If the user's role is 'admin', navigation is allowed. Otherwise, they are redirected to /forbidden.

Example 3: Chaining Multiple Middleware

A route /profile/edit requires authentication and also needs to prefetch user data.

// Assume router and user store are set up

const requiresAuth = (to: RouteLocationNormalized, from: RouteLocationNormalized, next: NavigationGuardNext) => {
  if (isLoggedIn()) {
    next();
  } else {
    next('/login');
  }
};

const fetchUserData = async (to: RouteLocationNormalized, from: RouteLocationNormalized, next: NavigationGuardNext) => {
  try {
    const userData = await fetchUserProfile(); // Asynchronous operation
    // You might store this data in a store or attach it to the route object if your router allows
    // For simplicity here, let's assume it's globally available or stored.
    next();
  } catch (error) {
    console.error("Failed to fetch user data:", error);
    next('/error'); // Redirect on fetch failure
  }
};

const routes: Array<RouteRecordRaw> = [
  // ... other routes
  {
    path: '/profile/edit',
    component: EditProfileView,
    meta: {
      middleware: [requiresAuth, fetchUserData], // Chained middleware
    },
  },
  {
    path: '/error',
    component: ErrorView,
  }
];

Explanation:

When navigating to /profile/edit, requiresAuth runs first. If it passes, fetchUserData runs. If both pass, the component renders. If fetchUserData fails, the user is redirected to /error.

Constraints

  • Your solution must be implemented in TypeScript.
  • You should use the Vue Router 4 API.
  • The middleware functions should be synchronous or asynchronous.
  • The middleware system should be extensible and easy to add new middleware to routes.
  • Avoid modifying the core Vue Router source code.

Notes

  • Think about how to effectively pass navigation control (allow, redirect, abort) from middleware to the router. The next() function provided by Vue Router's navigation guards is key here.
  • Consider how asynchronous middleware should be handled. Vue Router's beforeEach guard handles promises returned by navigation guards.
  • The meta property of route definitions is the standard place to store custom data like middleware configurations.
  • You will likely need to create a helper function that takes your route definitions (including the meta.middleware array) and registers the appropriate beforeEach guard on your Vue Router instance.
  • Consider how to handle potential infinite redirect loops, though a full solution for this complex edge case might be out of scope for the primary implementation. Focus on the core middleware execution logic.
Loading editor...
typescript