How can I globally manage errors in tRPC ? Trying `redirect` in custom link does not seem to work

I want to be able to redirect a user when they become unauthenticated. Someone else mentioned to create some NextJS middleware which makes an API call for the user but then that will make an extra API call for every tRPC request. It feels like there should be someway I can centrally manage errors where if tRPC returns an 'UNAUTHORIZED' error for any procedure I can do a redirect to '/login' I have a monorepo with everything together but the apps will be deployed separately eventually.
import { redirect } from 'next/navigation'

export const customLink: TRPCLink<AppRouter> = () => {
return ({ next, op }) => {
return observable((observer) => {
const unsubscribe = next(op).subscribe({
next(value) {
observer.next(value)
},
error(err) {
observer.error(err)
if (err?.data?.code === 'UNAUTHORIZED') {
redirect('/login')
}
},
complete() {
observer.complete()
},
})
return unsubscribe
})
}
}
import { redirect } from 'next/navigation'

export const customLink: TRPCLink<AppRouter> = () => {
return ({ next, op }) => {
return observable((observer) => {
const unsubscribe = next(op).subscribe({
next(value) {
observer.next(value)
},
error(err) {
observer.error(err)
if (err?.data?.code === 'UNAUTHORIZED') {
redirect('/login')
}
},
complete() {
observer.complete()
},
})
return unsubscribe
})
}
}
I've tested this custom link before the httpLink but the redirect does not seem to ever run. The console logs as expected. Is it something to do with the error still being thrown ?
2 Replies
Alky
Alky12mo ago
I guess you would need to change the client side of TRPC to handle these error. Using this logic, you probably will change the QueryClient that TRPC is using. I made it work this way, but i think still adjustments because of the delay:
export const api = createTRPCNext<AppRouter>({
config() {
return {
transformer: superjson,
links: [
loggerLink({
enabled: (opts) =>
process.env.NODE_ENV === "development" ||
(opts.direction === "down" && opts.result instanceof Error),
}),
httpBatchLink({
url: `${getBaseUrl()}/api/trpc`,
}),
],
queryClient: new QueryClient({
queryCache: new QueryCache({
onError: (err: unknown) => {
if (err instanceof TRPCClientError) {
const error = err as TRPCClientError<AppRouter>
if(error.data?.code === "UNAUTHORIZED") {
window.location.href = "/login"
}
}
},
}),
mutationCache: new MutationCache({
onError: (err) => {
if (err instanceof TRPCClientError) {
const error = err as TRPCClientError<AppRouter>
if(error.data?.code === "UNAUTHORIZED") {
window.location.href = "/login"
}
}
},
})

})
};
},
ssr: false,
});
export const api = createTRPCNext<AppRouter>({
config() {
return {
transformer: superjson,
links: [
loggerLink({
enabled: (opts) =>
process.env.NODE_ENV === "development" ||
(opts.direction === "down" && opts.result instanceof Error),
}),
httpBatchLink({
url: `${getBaseUrl()}/api/trpc`,
}),
],
queryClient: new QueryClient({
queryCache: new QueryCache({
onError: (err: unknown) => {
if (err instanceof TRPCClientError) {
const error = err as TRPCClientError<AppRouter>
if(error.data?.code === "UNAUTHORIZED") {
window.location.href = "/login"
}
}
},
}),
mutationCache: new MutationCache({
onError: (err) => {
if (err instanceof TRPCClientError) {
const error = err as TRPCClientError<AppRouter>
if(error.data?.code === "UNAUTHORIZED") {
window.location.href = "/login"
}
}
},
})

})
};
},
ssr: false,
});
In other words, you can tweak react query to do something with global callbacks. I guess this is somewhat dangerous, because it's called for EVERY query or mutation. I also made some type narrowing, but i couldn't find a way to narrow err to TRPCClientError<AppRouter> instead of just TRPCClientError without a reassign.
BillyBob
BillyBobOP12mo ago
Thanks for taking the time to respond, in my case I am using app directory with a separated backend, but still within a monorepo. So I am not actually using react query
Want results from more Discord servers?
Add your server