giray
giray9mo ago

Global Error Catcher

I have an application that has many mutations and these mutations return an error if the user is not logged in. In that case I want to direct user to the login page, however, I do not want to modify all my client code to do this for a particular error. Is there a way to catch all errors on the client side (NextJS app router)?
15 Replies
Indy
Indy9mo ago
Component – React
The library for web and native user interfaces
Indy
Indy9mo ago
But I think what you actually want to do for authentication is something like this: https://docs.convex.dev/auth/clerk#logged-in-and-logged-out-views
Convex Clerk | Convex Developer Hub
Clerk is an authentication platform providing login via
giray
girayOP9mo ago
Thanks
Mordsith
Mordsith8mo ago
@giray Any luck with this? I need this exact flow but the links above didn't work. The call to mutation logs an error in the console from convex. I want to redirect when this happens. Error boundary didn't catch this
No description
Mordsith
Mordsith8mo ago
I throw new ConvexError("Unauthenticated") from convex. How do I catch this in one place on the client so I can redirect?
erquhart
erquhart8mo ago
The above links do work for me. An error boundary will catch any uncaught errors thrown from child components. It will not catch errors thrown from the same component that renders the error boundary, or obviously any errors thrown by parent components.
lee
lee8mo ago
i haven't tried this, but i think the documentation says that error boundaries only catch rendering errors, not errors from event handlers and async code, which mutations (usually?) are. https://legacy.reactjs.org/docs/error-boundaries.html#how-about-event-handlers you can catch a mutation's error with a
try {
await mymutation();
} catch (e) {
if (e instanceof ConvexError) {
// handle
}
}
try {
await mymutation();
} catch (e) {
if (e instanceof ConvexError) {
// handle
}
}
Error Boundaries – React
A JavaScript library for building user interfaces
lee
lee8mo ago
if you don't want to put a try-catch at each call-site, you could make a wrapper
function useAuthedMutation(...args) {
const m = useMutation(...args);
const redirect = useRedirect();
return m.catch((e) => {
if ((e instanceof ConvexError) && e.data === 'Unauthenticated') {
redirect('/login');
} else {
throw e;
}
});
}
function useAuthedMutation(...args) {
const m = useMutation(...args);
const redirect = useRedirect();
return m.catch((e) => {
if ((e instanceof ConvexError) && e.data === 'Unauthenticated') {
redirect('/login');
} else {
throw e;
}
});
}
erquhart
erquhart8mo ago
Gah, I thought this might be the case but the docs didn't confirm it, legacy docs ftw
Mordsith
Mordsith8mo ago
Thank you @lee That wrapper solution looks like what I needed.
Mordsith
Mordsith8mo ago
@lee I had a challenge with the wrapper approach and Typescript typings. This is how I use mutations (Image 1) When I introduce the wrapper (Image 2)
No description
No description
Mordsith
Mordsith8mo ago
The function is meant to be reusable, I updated it to match the original convex useMutation
No description
erquhart
erquhart8mo ago
You're not calling the mutation - try m(...args).catch
lee
lee8mo ago
oh yeah sorry i put the args in the wrong place. Thanks @erquhart
Mordsith
Mordsith8mo ago
Thank you @erquhart @lee