Middleware для маршрутов

Middleware в контексте TanStack Router представляет собой механизм, позволяющий перехватывать переходы между маршрутами, выполнять дополнительную обработку и изменять поведение маршрутизации. Middleware полезен для аутентификации, логирования, контроля доступа, перезапроса данных и других задач, где требуется вмешательство в процесс навигации.


Основные принципы работы Middleware

Middleware в TanStack Router работают на уровне маршрутов, то есть они могут быть привязаны как к отдельному маршруту, так и к группе маршрутов. Каждое такое промежуточное звено получает контекст маршрута, может выполнить асинхронные действия и контролировать дальнейший процесс перехода.

Ключевые моменты работы middleware:

  1. Контекст маршрута – объект, содержащий текущий путь, параметры маршрута, query-параметры и состояние навигации.
  2. Асинхронность – middleware может выполнять асинхронные операции, например, проверку токена пользователя через API.
  3. Прерывание перехода – middleware может остановить или перенаправить пользователя на другой маршрут.

Создание Middleware

Middleware реализуется как функция, принимающая объект контекста и возвращающая либо результат, либо промис. Стандартный синтаксис:

import { createRouter, RouterContext } from '@tanstack/router';

function authMiddleware({ context, next }) {
  const isAuthenticated = context.user?.isLoggedIn;
  if (!isAuthenticated) {
    return context.router.navigate('/login');
  }
  return next();
}

Здесь:

  • context – объект, содержащий данные маршрута, параметры, текущий router instance.
  • next() – функция, которая передает управление следующему middleware или завершает переход.

Подключение Middleware к маршрутам

Middleware можно подключать на нескольких уровнях:

1. На уровне отдельного маршрута

const protectedRoute = createRoute({
  path: '/dashboard',
  component: Dashboard,
  middleware: [authMiddleware],
});

2. На уровне группы маршрутов

const appRoutes = createRoute({
  path: '/',
  children: [
    protectedRoute,
    publicRoute,
  ],
  middleware: [logMiddleware],
});

Middleware на уровне группы маршрутов выполняются до middleware отдельных маршрутов.


Порядок выполнения Middleware

Порядок выполнения определяется иерархией маршрутов и порядком подключения middleware:

  1. Сначала выполняются middleware родительских маршрутов.
  2. Затем выполняются middleware дочерних маршрутов в порядке их подключения.
  3. Если какой-либо middleware прерывает переход (например, перенаправлением), последующие middleware не вызываются.

Примеры использования Middleware

1. Контроль доступа

function roleMiddleware({ context, next }) {
  if (context.user.role !== 'admin') {
    return context.router.navigate('/not-authorized');
  }
  return next();
}

Проверяет роль пользователя и перенаправляет на страницу с сообщением о недостаточных правах доступа.

2. Логирование переходов

function logMiddleware({ context, next }) {
  console.log(`Переход на маршрут: ${context.route.path}`);
  return next();
}

Фиксирует все маршруты, на которые выполняется переход.

3. Загрузка данных перед рендером

async function loadDataMiddleware({ context, next }) {
  context.data = await fetchData(context.route.params.id);
  return next();
}

Асинхронно загружает данные для компонента перед его рендером, сохраняя результат в контексте.


Асинхронная обработка и ошибки

Middleware может быть асинхронным, что позволяет выполнять запросы к серверу, проверку токенов или авторизацию через API:

async function asyncAuth({ context, next }) {
  try {
    const isValid = await verifyToken(context.user.token);
    if (!isValid) return context.router.navigate('/login');
  } catch (err) {
    return context.router.navigate('/error');
  }
  return next();
}

Ошибка в middleware может быть обработана через try/catch или глобальный обработчик ошибок маршрутизатора, что позволяет управлять поведением приложения в случае сбоев.


Передача данных через Middleware

Контекст маршрута (context) используется для передачи данных между middleware и компонентом маршрута:

function addUserData({ context, next }) {
  context.userData = { name: 'Alex', role: 'admin' };
  return next();
}

// В компоненте
function Dashboard({ context }) {
  console.log(context.userData.name); // Alex
}

Таким образом, middleware становится мощным инструментом для подготовки состояния перед рендером компонентов.


Рекомендации по организации Middleware

  1. Разделять middleware по назначению: аутентификация, логирование, загрузка данных.
  2. Использовать минимальные и атомарные middleware для упрощения тестирования и повторного использования.
  3. Придерживаться строгого порядка вызова и учитывать иерархию маршрутов.
  4. Обрабатывать ошибки и предусматривать fallback-пути для надежной навигации.

Middleware в TanStack Router обеспечивает гибкий контроль над маршрутизацией, позволяя строить сложные приложения с защитой маршрутов, асинхронной загрузкой данных и централизованной обработкой переходов. Их грамотная организация повышает читаемость кода и облегчает поддержку масштабных проектов.