Error Handling
Behandle Fehler in Nuxt elegant – von globalen Fehlerseiten über Error Boundaries bis hin zu API-Fehlern.
Fehlerbehandlung in Nuxt
Jede Anwendung muss mit Fehlern umgehen – sei es ein 404 für eine nicht gefundene Seite, ein API-Fehler oder ein unerwarteter Laufzeitfehler. Nuxt bietet ein durchdachtes Error-Handling-System mit mehreren Ebenen:
- error.vue – Globale Fehlerseite
- NuxtErrorBoundary – Lokale Fehler in Komponenten abfangen
- createError() – Fehler programmatisch auslösen
- showError() / clearError() – Fehler anzeigen und zurücksetzen
Rails-Vergleich
In Rails nutzt du rescue_from in Controllern und eigene Fehlerseiten in public/404.html oder app/views/errors/. Nuxt hat ein ähnliches Konzept: error.vue für die globale Fehlerseite und createError() statt raise ActiveRecord::RecordNotFound.
Die globale Fehlerseite: error.vue
Erstelle eine error.vue im Root-Verzeichnis deines Projekts (neben app.vue). Diese Seite wird automatisch angezeigt, wenn ein fataler Fehler auftritt:
<!-- error.vue -->
<script setup lang="ts">
const props = defineProps<{
error: {
statusCode: number
statusMessage?: string
message?: string
}
}>()
const handleError = () => clearError({ redirect: '/' })
</script>
<template>
<div class="min-h-screen flex items-center justify-center">
<div class="text-center">
<h1 class="text-6xl font-bold text-red-500">
{{ error.statusCode }}
</h1>
<p class="text-xl mt-4 text-gray-600">
{{ error.statusMessage || 'Ein Fehler ist aufgetreten' }}
</p>
<button
class="mt-8 px-6 py-3 bg-blue-500 text-white rounded-lg"
@click="handleError"
>
Zurück zur Startseite
</button>
</div>
</div>
</template>Kein Layout!
error.vue ist keine Seite im pages-Verzeichnis – sie ist eine eigenständige Komponente. Sie hat keinen Zugriff auf Layouts. Wenn du ein Layout brauchst, rendere es manuell innerhalb von error.vue.
Fehler auslösen: createError() & showError()
Mit createError() löst du Fehler aus, die Nuxt zur Fehlerseite weiterleiten. Das funktioniert sowohl auf dem Server als auch im Client:
<script setup lang="ts">
const route = useRoute()
const { data: post } = await useFetch(`/api/blog/${route.params.slug}`)
// Wenn der Post nicht existiert → 404-Fehlerseite
if (!post.value) {
throw createError({
statusCode: 404,
statusMessage: 'Artikel nicht gefunden',
message: `Der Artikel "${route.params.slug}" existiert nicht.`
})
}
</script>showError() zeigt die Fehlerseite an, ohne den aktuellen Rendering-Prozess abzubrechen. Nützlich, wenn du den Fehler manuell behandeln willst:
// composables/useApi.ts
export function useApi() {
async function fetchWithErrorHandling<T>(url: string): Promise<T | null> {
try {
const data = await $fetch<T>(url)
return data
} catch (err: any) {
if (err.statusCode === 401) {
// Zur Login-Seite weiterleiten
await navigateTo('/login')
return null
}
// Fehlerseite anzeigen
showError({
statusCode: err.statusCode || 500,
statusMessage: err.message || 'Serverfehler'
})
return null
}
}
return { fetchWithErrorHandling }
}createError() wirft auf dem Server einen HTTP-Error (z.B. 404, 500). Im Client zeigt es die error.vue-Seite an. showError() zeigt immer nur die Client-seitige Fehlerseite.
NuxtErrorBoundary – Lokale Fehlerbehandlung
Nicht jeder Fehler soll die ganze Seite ersetzen. Mit NuxtErrorBoundary fängst du Fehler in einem bestimmten Bereich ab und zeigst eine Fallback-UI an:
<template>
<div class="dashboard">
<h1>Dashboard</h1>
<!-- Hauptinhalt lädt normal -->
<DashboardStats />
<!-- Widget-Fehler crashen nicht die ganze Seite -->
<NuxtErrorBoundary>
<WeatherWidget />
<template #error="{ error, clearError }">
<div class="p-4 bg-red-50 rounded-lg">
<p class="text-red-600">Widget konnte nicht geladen werden</p>
<p class="text-sm text-red-400">{{ error.message }}</p>
<button
class="mt-2 text-sm text-blue-500 underline"
@click="clearError"
>
Erneut versuchen
</button>
</div>
</template>
</NuxtErrorBoundary>
<!-- Weitere Widgets unabhängig voneinander -->
<NuxtErrorBoundary>
<ActivityFeed />
<template #error="{ error }">
<p class="text-gray-400">Aktivitäten nicht verfügbar</p>
</template>
</NuxtErrorBoundary>
</div>
</template>Wann Error Boundaries nutzen?
Error Boundaries eignen sich für Bereiche, die unabhängig vom Rest der Seite funktionieren: Widgets, Sidebar-Inhalte, eingebettete Komponenten von Drittanbietern. Der Rest der Seite bleibt funktional, selbst wenn ein Widget fehlschlägt.
clearError() – Fehler zurücksetzen
clearError() setzt den aktuellen Fehlerzustand zurück und navigiert optional zu einer anderen Seite:
<script setup lang="ts">
const props = defineProps<{
error: { statusCode: number; statusMessage?: string }
}>()
// Option 1: Fehler löschen und zur Startseite
const goHome = () => clearError({ redirect: '/' })
// Option 2: Fehler löschen und aktuelle Seite neu laden
const retry = () => clearError()
// Option 3: Zur vorherigen Seite
const goBack = () => {
clearError()
const router = useRouter()
router.back()
}
</script> Wenn du clearError() ohne redirect aufrufst, stelle sicher, dass der Fehlerzustand wirklich behoben ist – sonst landest du in einer Endlosschleife.
API Error Handling
Fehler in API-Routen (server/api/) werden anders behandelt als Seitenfehler. Hier nutzt du createError aus dem H3-Framework:
// server/api/produkte/[id].get.ts
export default defineEventHandler(async (event) => {
const id = getRouterParam(event, 'id')
const produkt = await db.produkte.findUnique({
where: { id: Number(id) }
})
// 404 – Nicht gefunden
if (!produkt) {
throw createError({
statusCode: 404,
statusMessage: `Produkt ${id} nicht gefunden`
})
}
// 403 – Kein Zugriff
if (!produkt.veroeffentlicht) {
throw createError({
statusCode: 403,
statusMessage: 'Dieses Produkt ist nicht öffentlich'
})
}
return produkt
}) Auf der Client-Seite fängst du API-Fehler mit useFetch ab:
<script setup lang="ts">
const route = useRoute()
const { data: produkt, error } = await useFetch(
`/api/produkte/${route.params.id}`
)
// Fehler an Nuxt weiterleiten
if (error.value) {
throw createError({
statusCode: error.value.statusCode,
statusMessage: error.value.statusMessage
})
}
</script>
<template>
<div v-if="produkt">
<h1>{{ produkt.name }}</h1>
<p>{{ produkt.beschreibung }}</p>
</div>
</template>Rails-Vergleich
In Rails nutzt du rescue_from ActiveRecord::RecordNotFound und render json: { error: '...' }, status: :not_found. In Nuxt nutzt du createError() in API-Routen – die HTTP-Semantik ist identisch.
Custom Error Pages pro Statuscode
Du kannst in deiner error.vue verschiedene Designs pro Fehlercode anzeigen:
<script setup lang="ts">
const props = defineProps<{
error: { statusCode: number; statusMessage?: string; message?: string }
}>()
const errorInfo = computed(() => {
switch (props.error.statusCode) {
case 404:
return {
emoji: '🔍',
title: 'Seite nicht gefunden',
text: 'Die gesuchte Seite existiert leider nicht.',
}
case 403:
return {
emoji: '🔒',
title: 'Zugriff verweigert',
text: 'Du hast keine Berechtigung für diese Seite.',
}
case 500:
return {
emoji: '💥',
title: 'Serverfehler',
text: 'Etwas ist schiefgelaufen. Wir arbeiten daran!',
}
default:
return {
emoji: '😵',
title: 'Fehler',
text: props.error.statusMessage || 'Ein unbekannter Fehler.',
}
}
})
</script>
<template>
<div class="min-h-screen flex items-center justify-center">
<div class="text-center max-w-md">
<span class="text-8xl">{{ errorInfo.emoji }}</span>
<h1 class="text-3xl font-bold mt-6">{{ errorInfo.title }}</h1>
<p class="text-gray-500 mt-3">{{ errorInfo.text }}</p>
<div class="mt-8 flex gap-4 justify-center">
<button class="px-6 py-3 bg-gray-800 text-white rounded-lg" @click="clearError({ redirect: '/' })">
Startseite
</button>
<button class="px-6 py-3 border rounded-lg" @click="clearError()">
Erneut versuchen
</button>
</div>
</div>
</div>
</template>Zusammenfassung
| Situation | Nuxt-Lösung | Rails-Äquivalent |
|---|---|---|
| Globale Fehlerseite | error.vue | public/404.html |
| Fehler werfen | createError() | raise RecordNotFound |
| Fehler abfangen | NuxtErrorBoundary | rescue_from |
| API-Fehler | createError() in server/api | render status: :not_found |