BillyBob
BillyBob6mo ago

tRPC, NextJS 14, createTRPCProxyClient, How can I globally manage errors?

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.
12 Replies
BillyBob
BillyBob6mo ago
I have been trying with a custom link like below. But the redirect does not seem to run.
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 think its because the error is still being 'thrown'
Dani;
Dani;6mo ago
import { OperationLink, TRPCLink } from '@trpc/react-query';
import { observable, tap } from '@trpc/server/observable';

const errorLink: TRPCLink<AppRouter> = (): OperationLink<AppRouter> => {
const link: OperationLink<AppRouter> = ({ op, next }) => {
return observable((observer) => {
next(op)
.pipe(
tap({
error: (result) => {
if (result.data?.code === 'UNAUTHORIZED') {
navigate('/auth/login');
}
},
}),
)
.subscribe(observer);
});
};
return link;
};
import { OperationLink, TRPCLink } from '@trpc/react-query';
import { observable, tap } from '@trpc/server/observable';

const errorLink: TRPCLink<AppRouter> = (): OperationLink<AppRouter> => {
const link: OperationLink<AppRouter> = ({ op, next }) => {
return observable((observer) => {
next(op)
.pipe(
tap({
error: (result) => {
if (result.data?.code === 'UNAUTHORIZED') {
navigate('/auth/login');
}
},
}),
)
.subscribe(observer);
});
};
return link;
};
currently using this ^
BillyBob
BillyBob6mo ago
Thanks @Dani; Let me test! is navigate coming from useNavigate Because then it will need to be client side
Dani;
Dani;6mo ago
I'm using react-router-dom, it's an SPA in my case; you can just replace with whatever redirect function next.js provides you
BillyBob
BillyBob6mo ago
ah damn i cant use hooks as this is server side only
Dani;
Dani;6mo ago
Unsure if anything else is needed if you do SSR, haven't worked with next.js in a while ah
BillyBob
BillyBob6mo ago
ive tried loads now and cant find a good solution
Dani;
Dani;6mo ago
In your initial snippet you were importing redirect, can't you just use that?
import { redirect } from 'next/navigation'
import { redirect } from 'next/navigation'
so just replace navigate with redirect
BillyBob
BillyBob6mo ago
yeah it just doesn't work something to do with observables i think someone on the next support forum just said redirect will only work if next can catch the error thanks for your help though
Dani;
Dani;6mo ago
Unfortunate; hopefully someone else has a solution
K1|ller
K1|ller6mo ago
Well I manage everything from the main layout file, usually, I haven't found anything better, this for Vite or Next. This allows me to centralize the app a bit (besides redux). You can also use redux and have a error state or something, there are many ways to globally manage errors.
BillyBob
BillyBob6mo ago
Hi @Pablo M, thanks for sharing. Could you share a snippet of your layout file