Middleware
Intercepte Routen-Wechsel, schütze Seiten und führe Code vor jedem Seitenaufruf aus – ähnlich wie before_action in Rails-Controllern.
Was ist Middleware?
Middleware in Nuxt sind Funktionen, die vor dem Navigieren zu einer Route ausgeführt werden. Sie eignen sich perfekt für Authentifizierungs-Checks, Logging, Redirects oder das Laden von Daten, bevor eine Seite angezeigt wird.
Nuxt kennt drei Arten von Route-Middleware:
- Named Middleware – in
middleware/definiert, explizit pro Seite zugewiesen - Global Middleware – mit
.global-Suffix, läuft auf jeder Route - Inline Middleware – direkt in
definePageMetadefiniert
Rails-Vergleich
In Rails nutzt du before_action in Controllern, um Code vor einer Action auszuführen. Nuxt Middleware funktioniert ähnlich – sie läuft vor dem Rendern der Seite. Der Unterschied: Rails-Filter sind an Controller gebunden, Nuxt Middleware an Routen.
Named Middleware
Named Middleware wird im middleware/-Verzeichnis definiert und kann dann einzelnen Seiten zugewiesen werden. Der Dateiname bestimmt den Namen der Middleware.
export default defineNuxtRouteMiddleware((to, from) => {
const { loggedIn } = useUserSession()
// Wenn nicht eingeloggt, zur Login-Seite weiterleiten
if (!loggedIn.value) {
return navigateTo('/login')
}
}) Um diese Middleware einer Seite zuzuweisen, referenzierst du sie in definePageMeta:
<script setup lang="ts">
definePageMeta({
middleware: 'auth'
})
</script>
<template>
<div>
<h1>Dashboard</h1>
<p>Nur für eingeloggte Nutzer sichtbar</p>
</div>
</template>Mehrere Middleware
Du kannst auch mehrere Middleware als Array angeben: middleware: ['auth', 'admin']. Sie werden in der angegebenen Reihenfolge ausgeführt.
Global Middleware
Global Middleware läuft auf jeder Route automatisch. Benenne die Datei einfach mit dem Suffix .global:
export default defineNuxtRouteMiddleware((to, from) => {
console.log(`Navigation: ${from.path} → ${to.path}`)
// Optional: Analytics-Event senden
if (false) {
trackPageView(to.path)
}
})Ausführungsreihenfolge
Globale Middleware wird immer vor named/inline Middleware ausgeführt. Innerhalb der globalen Middleware bestimmt die alphabetische Reihenfolge der Dateinamen die Ausführung. Nutze Zahlen-Präfixe wie 01.auth.global.ts für explizite Kontrolle.
Inline Middleware
Für einfache, seitenspezifische Logik kannst du Middleware direkt in definePageMeta definieren, ohne eine separate Datei zu erstellen:
<script setup lang="ts">
definePageMeta({
middleware: [
function (to, from) {
const { isAdmin } = useUserSession()
if (!isAdmin.value) {
return abortNavigation()
}
}
]
})
</script>
<template>
<div>
<h1>Admin-Einstellungen</h1>
</div>
</template>Einschränkung
Inline Middleware wird zur Build-Zeit extrahiert und kann deshalb keine lokalen Variablen oder Imports aus dem <script setup>-Block referenzieren. Für komplexere Logik nutze named Middleware.
Server Middleware
Neben Route-Middleware gibt es auch Server-Middleware, die auf dem Server bei jedem API-Request ausgeführt wird. Diese lebt im server/middleware/-Verzeichnis:
export default defineEventHandler((event) => {
// CORS-Header für alle Server-Requests setzen
setResponseHeaders(event, {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
})
})Server-Middleware ist etwas anderes als Route-Middleware: Sie läuft auf dem Server für jeden Request (auch API-Routen), nicht nur bei Navigation. Vergleichbar mit Rack-Middleware in Rails.
Praxisbeispiel: Auth Guard
Hier ein vollständiges Beispiel für eine Authentifizierungs-Middleware, die den Login-Status prüft und nicht-authentifizierte Nutzer weiterleitet:
export default defineNuxtRouteMiddleware(async (to) => {
const { loggedIn, user, fetch: fetchSession } = useUserSession()
// Session laden, falls noch nicht vorhanden
if (!user.value) {
await fetchSession()
}
// Öffentliche Routen überspringen
const publicRoutes = ['/login', '/register', '/passwort-vergessen']
if (publicRoutes.includes(to.path)) {
return
}
// Nicht eingeloggt → Login-Seite
if (!loggedIn.value) {
return navigateTo('/login', {
redirectCode: 302,
// Ursprüngliche URL merken für Redirect nach Login
query: { redirect: to.fullPath }
})
}
})Rails-Äquivalent
In Rails würdest du das so lösen:
class ApplicationController < ActionController::Base
before_action :authenticate_user!
private
def authenticate_user!
unless current_user
session[:return_to] = request.fullpath
redirect_to login_path, alert: "Bitte einloggen"
end
end
endnavigateTo vs. abortNavigation
navigateTo() leitet den Nutzer um (wie redirect_to in Rails). abortNavigation() bricht die Navigation komplett ab und kann optional einen Fehler werfen.
Zusammenfassung
| Typ | Ort | Rails-Äquivalent |
|---|---|---|
| Named | middleware/name.ts | before_action :name, only: [:show] |
| Global | middleware/name.global.ts | before_action :name (ApplicationController) |
| Inline | definePageMeta | Lambda in before_action |
| Server | server/middleware/ | Rack-Middleware |