working with custom errors and trpc errorFormatter
Hey guys. I'm trying to refactor my app so that all errors extend a BaseError class to make dealing with error codes and user-friendly messages easier. I'm having trouble converting these to the correct shape in the tRPC errorFormatter. Right now when I throw these custom errors tRPC doesnt recognize them and converts all of them to INTERNAL_SERVER_ERROR's.
I see that some error codes get translated in 3 places: the jsonrpc error code, the http status code, and an HTTP status,. There also seems to be some additional structure. My end goal is to be able to convert my errors to the 'normal' shape so that trpc-openapi can correctly return them.
Is there a reasonable way to do this in tRPC or should I do something different?
Its easy enough for me to convert one of my errors to a TRPCError, but I don't see a way to convert that to the right shape once I'm in the errorFormatter. is there a way to do this?
5 Replies
It should pass through your errorFormatter and you can detect it and decide what to do with it. You probably want the
error.cause
property to access your thrown error
Try just console logging everything out to start and then go from thereI got that far, but it seems like I need to be able to get a few fields that i dont really know how to generate:
1) shape.code (which looks to be some kind of JSONRPC code?)
2) shape.data httpStatus (is there an easy way to get this from a TRPCError code?)
3) can i add an additional customAppCode somewhere in this structure? do i put it in the root of the object or shape.data? I'm not really sure what the implications of any of this are.
thanks for any help
Ok so i did some more looking into this and now im more confused
after some confusing results I added this to my errorFormatter():
this data exactly matches the data i get when i throw a regular TRPCError
this seems to work in regular trpc (although im still not sure how i should be generating all these codes)
but in trpc-openapi i always get a 500 if the original error isnt a TRPCError
ok so after a bunch of trying things im most of the way there with this code:
but trpc-openapi doesnt completely work. I can change the http status code with this
responseMeta()
call:
but this still doesnt change the code that is actually sent down in the body:
it seems that the TRPCError.code variable is also readonly, do i cant change it
im also not sure if im handling stack
correctly. will it still properly get omitted in production builds?It's really up to you how you use the errorFormatter, but I would avoid thinking in HTTP. This isn't REST, the whole point is to think in Typescript, so craft types that represent your errors and return them, then they'll be inferred and typesafe on the frontend
I'm not really very familiar with trpc-openapi though, it's not a project I'm involved in or have used
I'm not trying to think in HTTP. openapi-trpc should do that for me (at least thats the theory)
the problem im having is that i cant figure out how to take a non-trpc error in the error formatter and make it the same shape as a native trpc error
if i only ever threw TRPCErrors then everything would work fine (which is what my app has been doing up to this point)
but even if i know all the parameters that i would pass to a TRPCError, once its in the error formatter it seems that I lose the ability to replicate the same exact shape
i guess a simpler, but still practical, example would be like
lets say I have
TRPCError({message: "You aren't allowed to see this", code: 'UNAUTHORIZED'})
but then i want to enforce a rule in my error formatter that says 'for security reasons, i dont want to return UNAUTHORIZED because that tells the client that this resource exists. I want to convert this to NOT_FOUND'
as far as i can tell, i cant really do that
is that accurate?You don’t need to match the shape of the trpc error, there are a few keys that tRPC requests in the formatter that you can just copy over, but then you attach what you like and the frontend will get access
There’s no need to throw any specific error class yourself, it’s totally fine to use custom ones