Route Protection
The /favorites and /visited pages should redirect unauthenticated visitors to login.
In Next.js, a root middleware.ts can inspect the session and use matcher configuration to select protected routes.
Nuxt can apply named middleware per page through definePageMeta. Each protected page declares the middleware it uses, while public pages remain unchanged.
Outcome
Create an auth middleware and apply it to the favorites and visited pages.
Fast Track
- Create
app/middleware/auth.tsthat checksuseUserSessionand redirects - Add
definePageMeta({ middleware: "auth" })to the favorites and visited pages - Verify unauthenticated users get redirected to login
Hands-on exercise 3.3
Build the middleware and protect the authenticated pages.
Requirements:
- Create
app/middleware/auth.tsthat redirects to/loginif the user isn't logged in - Add
definePageMeta({ middleware: "auth" })toapp/pages/favorites.vue - Add
definePageMeta({ middleware: "auth" })toapp/pages/visited.vue - Verify unauthenticated users get redirected when visiting either page
Implementation hints:
defineNuxtRouteMiddlewarecreates a named middleware. The filename becomes the nameuseUserSession()works inside middleware because middleware runs in the Nuxt contextnavigateTo("/login")returned from middleware triggers a redirectdefinePageMetais a compiler macro likedefineProps. It runs at build time, not runtime
Here's the middleware:
export default defineNuxtRouteMiddleware((to) => {
const { loggedIn } = useUserSession();
if (!loggedIn.value) {
return navigateTo("/login");
}
});The middleware checks loggedIn and redirects when it is false. The to parameter is available when protection depends on the target route.
In Next.js, the equivalent would be in middleware.ts:
// Next.js middleware.ts: for comparison
import { NextResponse } from "next/server";
export function middleware(request) {
const session = request.cookies.get("session");
if (!session) {
return NextResponse.redirect(new URL("/login", request.url));
}
}
export const config = {
matcher: ["/favorites", "/visited"],
};Next.js selects routes through matcher configuration. Nuxt places that selection in each page's metadata.
Now apply the middleware to the pages that need it. Add definePageMeta to the favorites page:
<script setup lang="ts">
definePageMeta({
middleware: "auth",
});
</script>And the visited page:
<script setup lang="ts">
definePageMeta({
middleware: "auth",
});
</script>definePageMeta is a compiler macro, like defineProps. It gets extracted at build time and doesn't appear in the compiled component. The string "auth" matches the filename auth.ts in the middleware directory. Nuxt connects them automatically.
You can stack multiple middleware on a page with an array: middleware: ["auth", "admin"]. They run in order. If any middleware returns a redirect, the chain stops.
Try It
- Log out if you're currently logged in
- Visit
http://localhost:3000/favoritesdirectly. You should be redirected to/login - Visit
http://localhost:3000/visited. It should also redirect to/login - Visit
http://localhost:3000/springs. No redirect. The browse page is still public - Log in via GitHub. Visit
/favoritesand/visited. Both should load normally
Commit
git add -A && git commit -m "feat(auth): add route middleware to protect favorites and visited pages"Done-When
app/middleware/auth.tsredirects unauthenticated users to/login/favoritesand/visitedrequire authentication/springsand/loginremain public- You can explain the difference between named middleware and global middleware in Nuxt
Solution
export default defineNuxtRouteMiddleware((to) => {
const { loggedIn } = useUserSession();
if (!loggedIn.value) {
return navigateTo("/login");
}
});Add definePageMeta to both protected pages:
<script setup lang="ts">
definePageMeta({
middleware: "auth",
});
</script>
<template>
<div>
<h1>
Your Favorites
</h1>
<p>
Saved favorites will appear here once we add user features.
</p>
</div>
</template><script setup lang="ts">
definePageMeta({
middleware: "auth",
});
</script>
<template>
<div>
<h1>
Visited Springs
</h1>
<p>
Your visited springs will appear here once we add tracking.
</p>
</div>
</template>Was this helpful?