#3 Error Handling in Nuxt
My thoughts on the Error Handling chapter of my Nuxt book and forming book parts using AsciiDoc.
🌟 Welcome to NuxtDojo! 🌟
In this edition, I want to share some ideas I have on the Error Handling chapter of my book. In the ever-evolving landscape of SSR frameworks like Nuxt, error handling transcends the basic six types provided by JavaScript, extending them further so that they align with the unique needs and philosophies of the Nuxt framework.
In the Author's corner, I'll share insights on leveraging Asciidoctor—a powerful tool for organising and structuring long-form content—to divide the book into cohesive parts, each housing chapters thoughtfully grouped together to provide a seamless and comprehensive learning experience.
Happy reading!
Nuxter’s Corner
The Error Handling chapter will be included in the Nuxt Fundamentals part of the book. Given the full-stack nature of Nuxt, this topic will flow from server-side errors to client-side errors. Nuxt provides powerful utilities to tackle an array of potential errors from HTTP errors (4xx, 5xx), data fetching errors, authentication errors to server-side errors originating from the Nitro server.
At the center of Error Handling in Nuxt, we have in-built createError and showError utilities that work on both client-side and server-side.
Basic idea is that we show or create errors using these utilities, then we either redirect users on full-screen, custom error page or display specific error information on the same page while providing a call-to-action for users to recover from the error. We can also catch these errors in either server-side or client-side hooks available to us in Nuxt and further report them in Error Reporting & Monitoring tools.
I have prepared this initial diagram to capture the error handling landscape in Nuxt.
Custom Error Page
Both createError and showError helps us return appropriate error responses to clients, including status codes, error messages, and error payloads.
When we throw an error using createError or showError utilities, we can send users on a custom, generic error page. And when Nuxt redirect users on a custom error page, we can use useError composable to flesh out error details on ~/error.vue.
And finally, we can use clearError utility to get the user unstuck from the error page and redirect them on a working page, for example a home page, i.e. clearError({ redirect: '/homepage' })
Error Handling on Server-side
We can throw Nitro server-side errors using the same createError utility that we can use on the client-side as well, and display the custom error page. On server-side, by default, createError redirects users to the custom error page - while on client-side, createError provides a bit more, fine-grained control over whether to redirect or not.
// server/route/test.ts
export default eventHandler((event) => {
const id = getRouterParam(event, 'id')
if (!id)
throw createError({ message: 'Invalid id', statusCode: 422 })
})
Error Handling on Client-side
There’ll be use cases when we do not want to redirect users over to a generic error page, and rather show them specific error messages on the client-side itself. That’s when we can leverage the in-built component <NuxtErrorBoundary> provided by Nuxt.
<NuxtErrorBoundary> component emits @error event for us to handle if needed, and also provides #error slot to access error details that we can use within the <NuxtErrorBoundary> component.
//~/pages/topics.vue
//parent page catches errors from child-routes: ~/pages/topics/index.vue
<template>
<NuxtErrorBoundary @error="logError">
<template #error="{ error, clearError }">
You can display the error locally here: {{ error }}
<button @click="clearError">
This will clear the error.
</button>
</template>
<NuxtPage />
</NuxtErrorBoundary>
</template>
In cases where we do want the redirection to happen even on the client-side, we can pass the fatal: true option while creating error and createError will behave exactly the same way as it does on the server-side. We do not need to use <NuxtErrorBoundary> in that case.
Data Fetching and Error Handling
Nuxt doesn’t have any hooks to catch errors while using useFetch or useAsyncData to fetch data. However, both of these composable returns { error } ref that we can check to see if an error has occurred.
In case the error ref comes truthy, we can fall back to showError or createError utilities to handle data fetching errors.
// ~/pages/items.vue
const { error } = await useAsyncData('home', () => queryContent('/items').findOne())
if (error) {
throw createError({
statusCode: 404,
message: 'Content not found',
fatal: true
})
}
Error Handling in Route Middleware
Apart from above options, there is a slightly less known option to catch errors using abortNavigation(err) utility function.
This option works exclusively within Route Middleware.
When abortNavigation is used with an error string passed into it, it redirects users to the error page on every http request made to that page where this route-middleware is applied.
abortNavigation utility uses createError under the hood. This would mean that if needed, we can use createError or showError utility directly inside route middleware as well.
// ~/middleware/test.ts
export default defineNuxtRouteMiddleware((to, from) => {
// ...
if (!user.value.isAuthorized) {
return abortNavigation('Insufficient permissions.')
}
})
This was a high-level, broad outlook of error handling in Nuxt. In the book however, I see a bigger scope for this chapter and I am hoping to include:
the importance of logging and monitoring in error handling,
best practices for error logging and tracking client-side errors for debugging purposes,
the significance of testing error scenarios to ensure robust error handling,
strategies for writing unit tests, integration tests, and end-to-end tests to cover error cases,
best practices and recommendations for effective error handling in full-stack applications and
real-life examples and/or case-studies to illustrate concepts and techniques discussed in the chapter.
If the outline of this chapter has piqued your interest and left you eager for more, I invite you to subscribe to NuxtDojo newsletter. By subscribing, you'll gain access to exclusive updates about the progress of my book, as well as the opportunity to be among the first to receive the early release of each chapter.
Author’s Corner
In the Author’s corner, we will see how to divide a book into parts. There are three steps to divide your book into parts using Asciidoctor. Make sure your .adoc document:
is set to :doctype: book,
has at at least one section with Heading 1
has a theme applied to it.
Content Structure
A part is designated by a level 0 section title indicated by single equal sign =, equivalent of Heading 1 in Markdown. Chapter title comes second in a hierarchy that is indicated by double equal sign ==. Asciidoctor will use this exact hierarchy to generate the table of content.
// index.adoc
= Nuxt Fundamentals
== Error Handling in Nuxt
image::images/error-handling-in-nuxt-v2.jpeg[]
Heading Font Style
Create a new .yml file in the theme folder, book-theme.yml where we will define the theme for our book.
Under the heading, we add h1 style that is applied to the Part headings, while the h2 styling is applied to the chapter headings.
// theme/book-theme.yml
heading:
h1:
align: center
padding: [4in, 0, 2in, 0]
font-size: 40
font-color: #05995d
font-weight: bold
top: 45%
h2:
font-size: 28
padding: [0.25in, 0, 0.5in, 0]
font-color: #18171c
Base Paragraph and Page Style
We can assign margins for all the pages and paragraphs, as well as base font attributes for overall book fonts - excluding heading fonts.
// theme/book-theme.yml
prose_margin_top: 0
prose_margin_bottom: 14
page:
margin: [1in, 1in, 1in, 1in]
base:
font-size: 15
font-color: #18171C
font-style: normal
line_height: 1.25
Title Page
In the same theme file, we can add title_page styling to format the second page of the book after the front cover page.
// theme/book-theme.yml
title_page:
align: center
title:
font-size: 40
font-color: #18171C
font-style: black
line_height: 1.5
top: 45%
Update Book Settings
Since Parts can only be used when the document type is a book, make sure :doctype: book is set along with :sectnums: and :partnums:, so that Asciidoctor can automatically number the parts and chapters in our book.
Make sure to use :toc: key here to help Asciidoctor generate the table of content at the beginning of the book.
In case, you want to call book parts something else other than Part, you can use :part-signifier: key.
// index.adoc
= Nuxt Your Everyday Vue.js
Krutie Patel <doc@example.com>
:toc:
:description: Book description goes Here
:doctype: book
:front-cover-image: image:images/isometric-nuxt-101.jpg[fit=fill]
:sectnums:
:partnums:
// :part-signifier: Module
We have a basic theme file ready. Now finally, update your script to accept this theme file path - theme/book-theme.yml and run yarn pdf to generate the PDF file of your book.
{
"name": "asciidoc-test",
"version": "0.0.1",
"main": "index.adoc",
"scripts": {
"pdf": "asciidoctor-pdf -a pdf-theme=book -a pdf-themesdir=theme/ index.adoc"
}
}
Thank you for joining me on this adventure, and I hope you find inspiration and enlightenment within these pages.
🚀 How You Can Participate:
📚 Read Along: Subscribe to the newsletter to receive updates, diagrams, and behind-the-scenes content.
🤝 Community Interaction: Share topic suggestions for the book, questions, and insights here on NuxtDojo Substack.
🚀 Spread the Word: Let others know about NuxtDojo – share on social media and among your developer circles.
Warm regards,
Krutie @ NuxtDojo 🚀📚