brand logo

ドキュメント

middleware

Nuxtは特定のルートに移動する前にコードを実行するためのミドルウェアを提供します。

Nuxtは、アプリケーション全体で使用できるカスタマイズ可能なルートミドルウェアフレームワークを提供しており、特定のルートに移動する前に実行したいコードを抽出するのに理想的です。

ルートミドルウェアには3種類あります:

  1. 匿名(またはインライン)ルートミドルウェアはページ内で直接定義されます。
  2. 名前付きルートミドルウェアは middleware/ に配置され、ページで使用されると非同期インポートを介して自動的にロードされます。
  3. グローバルルートミドルウェアは middleware/.global サフィックスを付けて配置され、すべてのルート変更時に実行されます。

最初の2種類のルートミドルウェアは definePageMeta で定義できます。

ミドルウェアの名前はケバブケースに正規化されます:myMiddlewaremy-middleware になります。

ルートミドルウェアはNuxtアプリのVue部分内で実行されます。似た名前ですが、サーバーミドルウェアとは完全に異なり、アプリのNitroサーバー部分で実行されます。

使用法

ルートミドルウェアは、現在のルートと次のルートを引数として受け取るナビゲーションガードです。

middleware/my-middleware.ts
export default defineNuxtRouteMiddleware((to, from) => {
  if (to.params.id === '1') {
    return abortNavigation()
  }
  // 実際のアプリではすべてのルートを `/` にリダイレクトすることはないでしょう
  // しかし、リダイレクトする前に `to.path` を確認することが重要です
  // さもないと無限リダイレクトループに陥る可能性があります
  if (to.path !== '/') {
    return navigateTo('/')
  }
})

Nuxtは、ミドルウェアから直接返すことができる2つのグローバルに利用可能なヘルパーを提供します。

  1. navigateTo - 指定されたルートにリダイレクトします
  2. abortNavigation - ナビゲーションを中止し、オプションでエラーメッセージを表示します。

vue-routerナビゲーションガードとは異なり、3番目の next() 引数は渡されず、リダイレクトまたはルートキャンセルはミドルウェアから値を返すことで処理されます

返すことができる値は次のとおりです:

  • 何も返さない(単純な return または何も返さない) - ナビゲーションをブロックせず、次のミドルウェア関数に移動するか、ルートナビゲーションを完了します
  • return navigateTo('/') - 指定されたパスにリダイレクトし、サーバーサイドでリダイレクトが発生した場合はリダイレクトコードを 302 Found に設定します
  • return navigateTo('/', { redirectCode: 301 }) - 指定されたパスにリダイレクトし、サーバーサイドでリダイレクトが発生した場合はリダイレクトコードを 301 Moved Permanently に設定します
  • return abortNavigation() - 現在のナビゲーションを停止します
  • return abortNavigation(error) - エラーで現在のナビゲーションを拒否します
こちらも参照 api > utils > navigate-to こちらも参照 api > utils > abort-navigation

リダイレクトやナビゲーションの停止を行うには、上記のヘルパー関数を使用することをお勧めします。vue-routerのドキュメントで説明されている他の可能な返り値も動作するかもしれませんが、将来的に破壊的な変更があるかもしれません。

ミドルウェアの順序

ミドルウェアは次の順序で実行されます:

  1. グローバルミドルウェア
  2. ページで定義されたミドルウェアの順序(配列構文で複数のミドルウェアが宣言されている場合)

例えば、次のようなミドルウェアとコンポーネントがあるとします:

middleware/ directory
-| middleware/
---| analytics.global.ts
---| setup.global.ts
---| auth.ts
pages/profile.vue
definePageMeta({
  middleware: [
    function (to, from) {
      // カスタムインラインミドルウェア
    },
    'auth',
  ],
});

ミドルウェアは次の順序で実行されることが期待されます:

  1. analytics.global.ts
  2. setup.global.ts
  3. カスタムインラインミドルウェア
  4. auth.ts

グローバルミドルウェアの順序

デフォルトでは、グローバルミドルウェアはファイル名に基づいてアルファベット順に実行されます。

しかし、特定の順序を定義したい場合があります。例えば、前述のシナリオでは、setup.global.tsanalytics.global.ts の前に実行される必要があるかもしれません。その場合、グローバルミドルウェアに「アルファベット」番号を付けることをお勧めします。

Directory structure
-| middleware/
---| 01.setup.global.ts
---| 02.analytics.global.ts
---| auth.ts

「アルファベット」番号が初めての場合、ファイル名は数値ではなく文字列としてソートされることを覚えておいてください。例えば、10.new.global.ts2.new.global.ts の前に来ます。これが、例で一桁の数字に 0 を付けている理由です。

ミドルウェアが実行されるタイミング

サイトがサーバーレンダリングまたは生成されている場合、初期ページのミドルウェアはページがレンダリングされるときとクライアントで再度実行されます。これは、ミドルウェアがブラウザ環境を必要とする場合、例えば生成されたサイトがある場合、レスポンスを積極的にキャッシュする場合、またはローカルストレージから値を読み取りたい場合に必要かもしれません。

しかし、この動作を避けたい場合は、次のようにできます:

middleware/example.ts
export default defineNuxtRouteMiddleware(to => {
  // サーバーでミドルウェアをスキップ
  if (import.meta.server) return
  // クライアント側でミドルウェアを完全にスキップ
  if (import.meta.client) return
  // または初期クライアントロード時のみミドルウェアをスキップ
  const nuxtApp = useNuxtApp()
  if (import.meta.client && nuxtApp.isHydrating && nuxtApp.payload.serverRendered) return
})

これは、サーバーでミドルウェア内でエラーをスローし、エラーページがレンダリングされる場合でも当てはまります。ミドルウェアはブラウザでも再度実行されます。

エラーページのレンダリングは完全に別のページロードであり、登録されたミドルウェアは再度実行されます。ミドルウェア内で useError を使用してエラーが処理されているかどうかを確認できます。

ミドルウェアを動的に追加する

addRouteMiddleware() ヘルパー関数を使用して、プラグイン内などからグローバルまたは名前付きルートミドルウェアを手動で追加することが可能です。

export default defineNuxtPlugin(() => {
  addRouteMiddleware('global-test', () => {
    console.log('このグローバルミドルウェアはプラグインで追加され、すべてのルート変更時に実行されます')
  }, { global: true })

  addRouteMiddleware('named-test', () => {
    console.log('この名前付きミドルウェアはプラグインで追加され、同じ名前の既存のミドルウェアを上書きします')
  })
})

Directory Structure
-| middleware/
---| auth.ts

ページファイル内で、このルートミドルウェアを参照できます:

definePageMeta({
  middleware: ["auth"]
  // または middleware: 'auth'
})

これで、そのページへのナビゲーションが完了する前に、auth ルートミドルウェアが実行されます。

サンプルコードの編集とプレビューexamples > routing > middleware

ビルド時にミドルウェアを設定する

各ページで definePageMeta を使用する代わりに、pages:extend フック内で名前付きルートミドルウェアを追加できます。

nuxt.config.ts
import type { NuxtPage } from 'nuxt/schema'

export default defineNuxtConfig({
  hooks: {
    'pages:extend' (pages) {
      function setMiddleware (pages: NuxtPage[]) {
        for (const page of pages) {
          if (/* some condition */ true) {
            page.meta ||= {}
            // これはページ内の `definePageMeta` で設定されたミドルウェアを上書きすることに注意してください
            page.meta.middleware = ['named']
          }
          if (page.children) {
            setMiddleware(page.children)
          }
        }
      }
      setMiddleware(pages)
    }
  }
})

tips

このセクションは公式ドキュメントの翻訳ではなく、本サイト独自の補足記事です。

Nuxtのルートミドルウェアとは?〜何が嬉しいのか〜

Nuxtのルートミドルウェアは、ユーザーがページを移動する際に特定の処理を挟むことができる仕組みです。例えば、認証チェックやアクセス制御、ページ遷移時のログ記録など、ルート変更の直前に実行したい処理をまとめて管理できます。

これにより、以下のような課題を解決できます。

  • ページごとに共通の前処理を重複なく実装できる
  • 認証状態に応じてアクセス制御を簡単に実装できる
  • ページ遷移の制御(リダイレクトや遷移キャンセル)がシンプルに行える

特に大規模なアプリケーションでは、ルートミドルウェアを活用することでコードの保守性が向上し、ユーザー体験の一貫性を保ちやすくなります。

まず結論:ルートミドルウェアのポイント

  • 3種類のミドルウェアがある

    1. ページ内で直接定義する匿名(インライン)ミドルウェア
    2. middleware/ フォルダに置く名前付きミドルウェア
    3. .global サフィックスを付けたグローバルミドルウェア(全ルートで実行)
  • リダイレクトや遷移キャンセルはミドルウェアから値を返すだけでOK
    navigateTo()abortNavigation() を返すことで制御可能

  • ミドルウェアはグローバル→ページ単位の順で実行される
    複数のミドルウェアを配列で指定した場合は順番に実行される

  • サーバー・クライアント両方で実行されることに注意
    必要に応じて環境判定をして処理を分けることができる

いつ使うべきか?使わない方がよいケースは?

使うべきケース

  • 認証・認可のチェック
    ログインしていないユーザーを特定ページからリダイレクトしたい場合

  • アクセス制御や権限管理
    ユーザーの権限に応じてアクセス可能なページを制限したいとき

  • ページ遷移時の共通処理
    ページ遷移のたびにログを送信したり、設定を初期化したりする場合

  • 動的なリダイレクト制御
    URLパラメータや状態に応じて遷移先を動的に変更したいとき

使わない方がよいケース

  • 単純なUI制御やコンポーネント内の状態管理
    ミドルウェアはルート遷移の制御に特化しているため、UIの細かい制御はコンポーネント内で行うべき

  • サーバーサイドのみで完結する処理
    NuxtのルートミドルウェアはVueのクライアント側でも動作するため、サーバー専用処理はサーバーミドルウェアを使うほうが適切

  • パフォーマンスに影響を与える重い処理
    ミドルウェアは遷移のたびに実行されるため、重い処理は避けるか非同期に分離することを検討

実務でよくあるユースケースとサンプルコード

1. 認証チェックによるアクセス制御

ログインしていないユーザーをログインページにリダイレクトする例です。

export default defineNuxtRouteMiddleware((to, from) => {
  const user = useAuthUser() // 仮想の認証状態取得関数
  if (!user.value && to.path !== '/login') {
    return navigateTo('/login')
  }
})

2. 管理者専用ページの権限チェック

ユーザーの権限が管理者でなければトップページに戻す例。

export default defineNuxtRouteMiddleware((to, from) => {
  const user = useAuthUser()
  if (user.value?.role !== 'admin') {
    return navigateTo('/')
  }
})

3. ページ遷移時のログ送信

ページ遷移のたびにログを送信するグローバルミドルウェア例。

export default defineNuxtRouteMiddleware(() => {
  sendPageViewLog() // 仮想のログ送信関数
})

よくある落とし穴・注意点

SSRとCSRでの挙動の違い

ミドルウェアはサーバーサイドレンダリング(SSR)時とクライアントサイドレンダリング(CSR)時の両方で実行されます。
そのため、ブラウザのAPI(windowlocalStorage)を使う場合は環境判定が必須です。

if (process.client) {
  // クライアント限定の処理
}

Hydration時の二重実行に注意

初回ロード時はサーバーでミドルウェアが実行され、その後クライアントで再度実行されることがあります。
これにより副作用が二重に発生する可能性があるため、nuxtApp.isHydrating を使って制御することも検討してください。

無限リダイレクトのリスク

ミドルウェア内でリダイレクトを行う際は、遷移先のパスを必ずチェックしましょう。
チェックを怠ると、同じページへのリダイレクトが繰り返されて無限ループになります。

if (to.path !== '/login') {
  return navigateTo('/login')
}

パフォーマンスへの影響

ミドルウェアはルート遷移のたびに実行されるため、重い処理やAPIコールを直接書くとUXが悪化します。
必要に応じてキャッシュや非同期処理の工夫をしましょう。

まとめ

Nuxtのルートミドルウェアは、ページ遷移前に処理を挟みたいときに非常に便利な機能です。
認証チェックやアクセス制御、共通処理の実装に役立ちますが、SSR/CSRの違いや無限リダイレクトなどの落とし穴にも注意が必要です。

適切に使いこなすことで、アプリケーションの保守性とユーザー体験を大きく向上させられます。
まずは簡単な認証チェックから試し、徐々に実務のニーズに合わせて活用してみてください。