'How to catch server errors in Nuxt.js so it doesn't crash page render? (Vue)
Context
This question is related to my other question, How to handle apollo client errors crashing page render in Nuxt? , but I'll try to keep this isolated since I'd like this question focused only on Nuxt (minus apollo). However, I decided to ask this separate since I'm looking for an entirely different response/solution.
The problem
I'm currently maintaining a production Nuxt/Vue app that is using the @nuxt/apollo
module to make GraphQL requests.
The problem, is that every now and then, the GraphQL server we rely on goes down and returns an HTML error page, which crashes the Apollo client. But because we're loading Apollo as a nuxt module, it crashes the page render pipeline as well. Giving us a generic server error page that looks like this;
Server error An error occurred in the application and your page could not be served. If you are the application owner, check your logs for details.
And the following stack trace:
ERROR Network error: Unexpected token < in JSON at position 0 08:11:04
at new ApolloError (node_modules/apollo-client/bundle.umd.js:92:26)
at node_modules/apollo-client/bundle.umd.js:1588:34
at node_modules/apollo-client/bundle.umd.js:2008:15
at Set.forEach (<anonymous>)
at node_modules/apollo-client/bundle.umd.js:2006:26
at Map.forEach (<anonymous>)
at QueryManager.broadcastQueries (node_modules/apollo-client/bundle.umd.js:2004:20)
at node_modules/apollo-client/bundle.umd.js:1483:29
at processTicksAndRejections (node:internal/process/task_queues:94:5)
However, none of this stack trace allows us to see where nuxt is throwing the error, so we can handle it.
What we tried
We've exhausted all our options looking into this issue for the past couple of weeks. We first tried to solve it by handling the error directly at Apollo level using all 3 apollo library abstractions's error handling solutions:
@nuxt/apollo
modulevue-apollo
apollo-client
If you'd like to read up more on that (even though its kind of irrelevant to this question), you can read more on my original question here
However, right now I'd prefer to know if there's a way to somehow handle these page render errors either by:
- Making the errors fail silently, so the page still renders as normal
- Allowing us to redirect to another page.
Since the apollo nuxt module we are using currently isn't working for that, I'd like to know if Nuxt supports some kind of way to handle errors.
It didn't help much that Nuxt's documentation is pretty limited when it comes to error handling. At best, it has information regarding the error pages and how to redirect to the error pages using context.error
. But it doesn't have a dedicated page on how to catch common errors. I have a feeling Nuxt hooks could be the answer, but documentation on them is hard to navigate and also sparse.
The most complete information source I found on nuxt error handling was this article, Error handling in NuxtJS, of which nothing suggested worked for us.
Summary
Our nuxt app is crashing when the @nuxt/apollo
nuxt module we are using crashes. We'd like to know if there's some kind of standard nuxt way of catching it, or if the only solution possible is just migrating our entire app to not use @nuxt/apollo
module and use the ES6 promise syntax and load apollo-client
manually into the app as a standalone library that's not deeply integrated into the nuxt lifecycle.
Solution 1:[1]
EDIT: I myself think that the problem lies somewhere in the Vue Apollo plugin or Nuxt Apollo module and how errors are handled there. I would think you can handle the error directly at the Apollo module but that is not possible in SSR.
You have to keep in mind that you probably need another solution for both CSR as well as for SSR.
In short, what happens is that the renderRoute
fails and because of this SSR ends up in the default errorMiddleware
of Nuxt.
1: Calling Apollo query directly, this gives you full control over the error handling for that page and will work for both CSR and for SSR
export default {
mounted() {
if (!this.books.length) {
// client side
this.fetchBooks()
}
},
serverPrefetch() {
this.fetchBooks()
},
methods: {
fetchBooks() {
this.$apollo
.query({
query: gql`
query books {
books {
title
author
test
}
}
`,
})
.catch((e) => {
console.log(e)
})
.then((data) => {
/// set books
})
},
},
}
2: Add a errorMiddleware
hook and handle the error there. This only works for SSR. Important to understand is that rendering failed so you have to redirect or render another page.
//nuxt.config.js
hooks: {
render: {
errorMiddleware(app) {
app.use((error, req, res, next) => {
res.writeHead(307, {
Location: '/network-error',
})
res.end()
})
},
},
},
3: Return false in the error
method of Apollo, This only works for CSR
export default {
apollo: {
books: {
query() {
return gql`
query books {
books {
title
author
test
}
}
`
},
error() {
return false
},
},
},
}
Solution 2:[2]
To prevent pages from being crashed, you need to handle errors and isolate them so they won't affect the rendering of other components. This can be achieved with the error handling concept of ErrorBoundary
. You can create a common component to reuse the ErrorBoundary
logic. There are several other benefits of this approach.
- Helps to keep components free from error handling logic
- Allows us to use declarative component composition instead of relying on imperative try/catch
- We can be as granular as we want with it — wrap individual components or entire application pieces.
Here is the example of how to create an error boundary.
export default {
name: 'ErrorBoundary',
data: () => ({
error: false
}),
errorCaptured (err, vm, info) {
this.error = true
},
render (h) {
return this.error ? h('p', 'Something went wrong') : this.$slots.default[0]
}
}
Now, you can wrap any component to the error boundary and isolate the error as given,
<error-boundary>
<counter />
</error-boundary>
Caveats of ErrorBoundary
component
There are some caveats when utilizing the errorCaptured
hook. Currently, errors are only captured in:
- render functions
- watcher callbacks
- lifecycle hooks
- component event handlers
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 | Shea Hunter Belsky |