Middleware

Some routes require checks before they can be accessed, such as verifying that a user is logged in before loading a dashboard.

Middleware runs during navigation. It can inspect the destination and current route, and can redirect to another path.

Creating Middleware

Create middleware with defineRouterMiddleware. It accepts a function that runs before navigation:

import { defineRouterMiddleware } from 'retend/router';

// A simple middleware that just logs where the user is going
const loggingMiddleware = defineRouterMiddleware((details) => {
  console.log(`User is going to: ${details.to.path}`);
});

The details object passed to your function contains two important properties:

  • to: Information about where the user is trying to go.
  • from: Information about the current page they are leaving.

Protecting Routes (Redirects)

A common use for middleware is authentication. If a user requests a protected route without an active session, return a redirect to the login page:

import { defineRouterMiddleware, redirect } from 'retend/router';

const authMiddleware = defineRouterMiddleware((details) => {
  // Check if they are trying to access an admin area
  if (details.to.path.startsWith('/admin')) {
    const isLoggedIn = checkUserSession(); // Your own logic

    if (!isLoggedIn) {
      // Stop the navigation and go to the login page instead
      return redirect('/login');
    }
  }

  // If you don't return anything, the navigation continues normally
});

When middleware returns redirect(), Retend cancels the original navigation and starts navigation to the redirect path.

Asynchronous Checks

When access depends on a server check, such as session-token verification, define the middleware function as async:

import { defineRouterMiddleware, redirect } from 'retend/router';

const permissionMiddleware = defineRouterMiddleware(async (details) => {
  if (details.to.path.startsWith('/account')) {
    // Wait for the server to verify the token
    const hasAccess = await verifyTokenWithServer();

    if (!hasAccess) {
      return redirect('/access-denied');
    }
  }
});

Retend waits for the Promise to settle before completing the navigation.

Using Multiple Middlewares

A Router can use multiple middleware functions. They run in the order listed. If one returns a redirect, subsequent middleware is not run for that navigation.

import { Router } from 'retend/router';

const router = new Router({
  routes: myRoutes,
  middlewares: [
    loggingMiddleware, // Runs 1st: logs the attempt
    authMiddleware, // Runs 2nd: checks if logged in
    permissionMiddleware, // Runs 3rd: checks specific permissions
  ],
});

Separate concerns by assigning each check to its own middleware function. For example, logging middleware can run independently of authentication middleware.