Middleware: проверки перед переходом

Middleware — это охранник на входе в маршрут: он запускается до отрисовки страницы и может пропустить, перенаправить или отменить переход.
Суть: route middleware — функция, выполняемая перед переходом на маршрут. Она получает целевой и исходный маршруты и решает: пропустить, перенаправить через navigateTo или отменить через abortNavigation. Используется для авторизации и проверок доступа.

Часто перед показом страницы нужно что-то проверить: вошёл ли пользователь, есть ли у него права, существует ли запрашиваемая сущность. Размазывать эти проверки по компонентам неудобно. Nuxt предлагает middleware — функции-перехватчики, которые выполняются до рендеринга маршрута.

Middleware живут в папке middleware/ и объявляются через defineNuxtRouteMiddleware. Функция получает два аргумента: to (куда идём) и from (откуда). Вернув navigateTo("/login"), вы перенаправите пользователя; вернув abortNavigation() — отмените переход; ничего не вернув — пропустите.

// middleware/auth.ts
export default defineNuxtRouteMiddleware((to, from) => {
  const user = useUserState()           // ваше состояние
  if (!user.value) {
    return navigateTo("/login")         // не вошёл -> на вход
  }
  // вошёл -> ничего не возвращаем, пускаем дальше
})

Подключить такое middleware к странице можно через definePageMeta:

<script setup>
definePageMeta({ middleware: "auth" })
</script>

Есть и глобальные middleware: файл с суффиксом .global.ts (например middleware/log.global.ts) выполняется перед каждым переходом без явного указания.

   Поток перехода на маршрут

   клик по NuxtLink
        |
        v
   [middleware] --(navigateTo)--> редирект на другой роут
        |        --(abortNavigation)--> переход отменён
        | (ничего не вернул)
        v
   рендер целевой страницы

Полезно различать два уровня обёрток. app.vue — это самый внешний контейнер на всё приложение целиком: он есть всегда и оборачивает любой макет. Сами макеты в layouts/ — это сменные рамки уровнем ниже: публичная часть, админка, лендинг без шапки. Такое двухуровневое деление позволяет держать глобальное (например, тост-уведомления или модальные окна) в app.vue, а вариативное оформление страниц — в макетах. Когда структура усложняется, это разделение спасает от дублирования и путаницы.

Полезно помнить, что middleware бывает трёх видов по месту срабатывания. Анонимное (инлайн) задаётся прямо в definePageMeta функцией — удобно для разовой проверки одной страницы. Именованное лежит файлом в middleware/ и подключается по имени к нужным страницам — это переиспользуемый гейт вроде auth. Глобальное с суффиксом .global срабатывает на каждый переход без явного указания — для сквозной логики. Выбор между ними — это баланс между точечностью и охватом: не делайте глобальным то, что нужно лишь паре страниц.

Как работает под капотом

Middleware встраивается в navigation guard клиентского и серверного роутера. При SSR оно отрабатывает на сервере перед рендером, при клиентской навигации — в браузере перед сменой компонента. Поэтому проверки доступа надёжны в обоих режимах. Возврат navigateTo прерывает текущий переход и начинает новый; abortNavigation просто останавливает его.

Смоделируем цепочку middleware — каждый шаг может пропустить или прервать переход:

// Конвейер проверок перед переходом на маршрут.
function runMiddleware(chain, to, user) {
  for (const mw of chain) {
    const result = mw(to, user);
    if (result) {
      return { blocked: true, reason: result };  // редирект/отмена
    }
  }
  return { blocked: false };  // все пропустили
}

const auth  = (to, user) => user ? null : "redirect:/login";
const admin = (to, user) => (user && user.role === "admin") ? null : "abort:403";

console.log("Гость на /profile:",
  runMiddleware([auth], "/profile", null));
console.log("Юзер на /admin:",
  runMiddleware([auth, admin], "/admin", { role: "user" }));
console.log("Админ на /admin:",
  runMiddleware([auth, admin], "/admin", { role: "admin" }));

Попробуй сам ▶ — конвейер останавливается на первой неуспешной проверке, как и реальные middleware Nuxt.

Частые ошибки

  • Не возвращать результат. Чтобы перенаправить, нужно именно return navigateTo(...), а не просто вызвать функцию.
  • Полагаться только на клиентскую проверку. Middleware — это UX-гейт, но настоящую авторизацию данных делайте ещё и на сервере в API.
  • Тяжёлые операции в middleware. Оно тормозит каждый переход — держите его лёгким.

Best practices

  • Именованные middleware — для конкретных защищённых страниц, глобальные — для сквозной логики (логирование, локаль).
  • Всегда дублируйте критичные проверки доступа на сервере: клиент можно обмануть.
  • Возвращайте navigateTo для редиректа и abortNavigation для жёсткой отмены.

Итог: middleware — это охрана на входе в маршрут. Она решает судьбу перехода до рендера, что идеально для авторизации. На этом раздел о маршрутизации завершён — переходим к компонентам и composables.

Проверьте себя
1. Что произойдёт, если route middleware вернёт navigateTo(\"/login\")?
AСтраница откроется, а потом перезагрузится
BТекущий переход прервётся и начнётся новый переход на /login
CНичего, navigateTo работает только в компонентах
DПриложение упадёт с ошибкой
2. Почему нельзя полагаться только на middleware для защиты данных?
AMiddleware работает медленно
BКлиентскую проверку можно обойти, поэтому реальную авторизацию данных нужно делать ещё и на сервере
CMiddleware не поддерживает редиректы
DMiddleware доступно только в Nuxt 2