【Nuxt.js】ルートミドルウェアでページ遷移を制御する

JavaScript

こんにちは、かつコーチです。

前回は[id].vueを使った動的ルート・ネストされたルートの書き方を解説しました。

今回は「ログインしていないユーザーを弾く」など、ページ遷移そのものを制御するルートミドルウェアを扱います。

「未ログインなのに管理画面が見えてしまう」という悩みを、この記事で解決します。

ルートミドルウェアとは?

用語の定義

ミドルウェアとは、ページが表示される前に割り込んで実行される処理のことです。

Nuxtのルートミドルウェアは、ページ遷移のたびに呼び出され、リダイレクトなどの制御を行えます。

middleware/ディレクトリにファイルを置くことで、その定義が行えます。

なぜ必要なのか

認証チェックを各ページのコンポーネントに毎回書くと、コードが重複し、書き忘れも発生します。

ミドルウェアとして共通化しておけば、「このページにはこのチェックを適用する」と1行指定するだけで済みます。

ページの中身とアクセス制御のロジックを分離できる点が、大きなメリットです。

基本の書き方

手順1:middleware/にファイルを作る

middleware/auth.tsというファイルを作成します。

// middleware/auth.ts
export default defineNuxtRouteMiddleware((to, from) => {
  const isLoggedIn: boolean = useCookie<boolean>("isLoggedIn").value ?? false

  if (!isLoggedIn && to.path !== "/login") {
    return navigateTo("/login")
  }
})

defineNuxtRouteMiddlewareはNuxtが提供する関数で、to(遷移先)とfrom(遷移元)の情報を受け取れます。

条件に一致したときはnavigateTo()で別ページへリダイレクトします。

手順2:definePageMetaでページに適用する

作成したミドルウェアは、使いたいページのdefinePageMetaで指定します。

<!-- pages/mypage/index.vue -->
<script setup lang="ts">
definePageMeta({
  middleware: "auth",
})
</script>

<template>
  <div>
    <h2>マイページ</h2>
  </div>
</template>

ファイル名auth.tsの拡張子を除いた"auth"という文字列で指定するのがポイントです。

つまずきやすい設定・注意点:ファイル名のtypo

私が実際にハマったのは、ミドルウェア名のスペルミスです。

<script setup lang="ts">
definePageMeta({
  middleware: "atuh", // "auth"のtypo
})
</script>

このコードを実行しても、ビルドエラーは一切出ませんでした。

しかし実際にアクセスしてみると、認証チェックがまったく効いておらず、未ログインでもページが表示されてしまいました。

存在しないミドルウェア名を指定してもNuxtは警告してくれないため、動作確認で気づくまで原因不明のバグとして扱ってしまいました。

middleware/配下のファイル名とdefinePageMetaの指定文字列は、必ず一致しているか目視で確認する癖をつけましょう。

よくあるつまずきポイント・エラー対処

名前付きミドルウェアとグローバルミドルウェアの違い

ミドルウェアには「指定したページだけに効く」ものと「全ページに自動で効く」ものの2種類があります。

❌ Before:全ページに認証チェックをかけたいのに、各ページへ指定して回っている

<!-- pages/index.vue -->
<script setup lang="ts">
definePageMeta({ middleware: "auth" })
</script>
<!-- pages/about.vue -->
<script setup lang="ts">
definePageMeta({ middleware: "auth" })
</script>

ページ数が増えるたびに書き忘れが発生し、実際に1ページだけ指定し忘れて認証が素通りしてしまったことがありました。

✅ After:ファイル名を.global.tsにして自動適用する

// middleware/auth.global.ts
export default defineNuxtRouteMiddleware((to) => {
  const isLoggedIn = useCookie<boolean>("isLoggedIn").value ?? false
  const publicPages: string[] = ["/login", "/"]

  if (!isLoggedIn && !publicPages.includes(to.path)) {
    return navigateTo("/login")
  }
})

ファイル名の末尾を.global.tsにするだけで、definePageMetaでの指定が不要になり、全ページに自動適用されます。

全体に一律でかけたいルールは.global.ts、特定ページだけに適用したいルールは名前付きミドルウェア、と使い分けるのがコツです。

応用・一歩先の使い方

複数のミドルウェアを組み合わせる

1つのページに複数のチェックをかけたい場合は、配列で指定できます。

<script setup lang="ts">
definePageMeta({
  middleware: ["auth", "admin-only"],
})
</script>

配列に書いた順番でミドルウェアが実行されるため、「先に認証を確認し、次に権限を確認する」といった順序制御が可能です。

インラインミドルウェアで1ページ限定の処理を書く

わざわざファイルを作るほどでもない、そのページ専用の処理はdefinePageMeta内に直接関数で書くこともできます。

<script setup lang="ts">
definePageMeta({
  middleware: [
    (to) => {
      if (to.query.preview !== "true") {
        return navigateTo("/")
      }
    },
  ],
})
</script>

インラインミドルウェアと呼ばれるこの書き方は、他のページで再利用しない一時的なロジックに向いています。

再利用する可能性が少しでもあるなら、最初からmiddleware/にファイルとして切り出しておくのがおすすめです。

まとめ

この記事のポイント

  • ミドルウェアはmiddleware/ディレクトリに置き、defineNuxtRouteMiddlewareで定義する
  • definePageMeta({ middleware: '名前' })で指定ページに適用する
  • ファイル名を.global.tsにすると、全ページへ自動適用されるグローバルミドルウェアになる
  • ミドルウェア名や配列指定のtypoはエラーにならず静かに無効化されるため、動作確認が欠かせない
  • 一時的な処理はdefinePageMeta内にインラインミドルウェアとして書くこともできる

次に読むべき記事

ミドルウェアで遷移制御ができるようになったら、次はuseFetchuseAsyncDataによるデータ取得を学んでいきましょう。

→ 次の記事:useFetch・useAsyncDataでデータを取得する(違いも解説)

タグ: Nuxt.js, 中級者向け, ルーティング

タイトルとURLをコピーしました