justindgm
justindgm
TtRPC
Created by justindgm on 11/28/2023 in #❓-help
Vanilla Client Error Handling
What is the right way to handle errors when using the vanilla client? If I setup a client like so:
import type { Router } from '../../../server/src/routers'

export const trpcClient = createTRPCProxyClient<Router>({
links: [
httpBatchLink({
url: `${env.SERVER_URI}/trpc`
})
]
})
import type { Router } from '../../../server/src/routers'

export const trpcClient = createTRPCProxyClient<Router>({
links: [
httpBatchLink({
url: `${env.SERVER_URI}/trpc`
})
]
})
And execute a mutation like so:
const user = await trpcClient.user.create.mutate({ email: string; password: string })
const user = await trpcClient.user.create.mutate({ email: string; password: string })
How should I handle errors? If I try to directly catch when calling the mutate function, the error is typed as any
const user = await trpcClient.user.create.mutate({ email: string; password: string }).catch((error) => {
// error is any
}
const user = await trpcClient.user.create.mutate({ email: string; password: string }).catch((error) => {
// error is any
}
Is there a way to type narrow the error at runtime? On the server side, I am using the errorFormatter to add a custom field to my errors:
const t = initTRPC.context<Context>().create({
isDev: env.NODE_ENV !== 'production',
errorFormatter: ({ shape, error }) => {
return {
...shape,
data: {
...shape.data,
customError: error.cause instanceof CustomError ? error.cause.customCode : null
}
}
}
})
const t = initTRPC.context<Context>().create({
isDev: env.NODE_ENV !== 'production',
errorFormatter: ({ shape, error }) => {
return {
...shape,
data: {
...shape.data,
customError: error.cause instanceof CustomError ? error.cause.customCode : null
}
}
}
})
My current approach will be to use a runtime validation library like zod to check if this custom field exists on the error
const user = await trpcClient.user.create.mutate({ email: string; password: string }).catch((error) => {
const parsedError = errorSchema.safeParse(error)
if (parsedError.isSuccess) {
console.error(parsedError.data.data.customError)
} else {
console.error(error)
}
}
const user = await trpcClient.user.create.mutate({ email: string; password: string }).catch((error) => {
const parsedError = errorSchema.safeParse(error)
if (parsedError.isSuccess) {
console.error(parsedError.data.data.customError)
} else {
console.error(error)
}
}
But this is clearly not ideal. Is this the right/best way to approach error handling with the vanilla client, or is there a better approach?
10 replies