こんにちは、かつコーチです。
前回は[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内にインラインミドルウェアとして書くこともできる
次に読むべき記事
ミドルウェアで遷移制御ができるようになったら、次はuseFetch・useAsyncDataによるデータ取得を学んでいきましょう。
→ 次の記事:useFetch・useAsyncDataでデータを取得する(違いも解説)
タグ: Nuxt.js, 中級者向け, ルーティング