fastify-type-provider-zod icon indicating copy to clipboard operation
fastify-type-provider-zod copied to clipboard

custom error handler unable to check for instanceof ResponseValidationError

Open nickramsbottom opened this issue 1 year ago • 4 comments

I am writing my own error handler for Fastify and want to intercept ResponseValidationErrors similar to the code example here: https://github.com/turkerdev/fastify-type-provider-zod/issues/26#issuecomment-1516147720. I'm using TypeScript.

When I try to check for the error using error instanceof ResponseValidationError I get false.

error.name === 'ResponseValidationError' is true so it is an instance of ResponseValidationError: https://github.com/turkerdev/fastify-type-provider-zod/blob/85675fad44c7679622d80fb1925f5901cc2f57f8/src/index.ts#L122

Am I missing something here? If the name is set correctly I'd expect the instanceof check to work.

My overall goal is to respond normally on response validation errors and log instead of returning errors to the client.

Code to reproduce below.

import { FastifyInstance } from 'fastify'
import {
	ResponseValidationError,
	ZodTypeProvider,
} from 'fastify-type-provider-zod'
import z, { ZodError } from 'zod'

type Params = {
	id: string
}

export default async function routes(fastify: FastifyInstance) {
	fastify.get('/', async () => ({ hello: 'world' }))
	fastify.withTypeProvider<ZodTypeProvider>().get<{ Params: Params }>(
		'/session/:id',
		{
			schema: {
				params: z.object({
					id: z.string().max(5),
				}),
				response: {
					200: z.number(),
				},
			},
		},
		async (request, reply) => {
			const { id } = request.params
			reply.send({ hello: id })
		},
	)

	fastify.setErrorHandler(function (error, request, reply) {
		fastify.log.warn(error)
		console.log('~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~')
		if (error instanceof ZodError) {
			console.log('ZodError')
		}

		if (error instanceof ResponseValidationError) {
			console.log('ResponseValidationError')
		}

		if (error.name === 'ResponseValidationError') {
			console.log('name is ResponseValidationError')
		}

		if (error instanceof Error) {
			console.log('Error')
		}
		console.log('~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~')

		reply.status(500).send({
			statusCode: 500,
			error: 'Failed successfully',
		})
	})
}

nickramsbottom avatar Sep 14 '23 12:09 nickramsbottom

I'll add a typeguard for these cases

kibertoad avatar May 20 '24 20:05 kibertoad

@kibertoad am I missing something fundamental with how instanceof works with classes? It looks like it's creating an instance of the class I linked so I'd expect error instanceof ResponseValidationError to be true. I'm starting to doubt I understand what's going on!

nickramsbottom avatar May 20 '24 21:05 nickramsbottom

@nickramsbottom instanceof is tricky in JavaScript, it is dependent on classes being from the same realm, see https://stackoverflow.com/questions/68564010/is-it-possible-several-javascript-realms-share-one-single-global-object

If you import a library A, and another library B imports a library A, and you use a class instantiated by library B using class from library A, there is a high chance they will be of a different realm, and won't pass instanceof check. for this reason I highly advise never to rely on instanceof for classes coming from external libraries, but use custom typeguards instead.

kibertoad avatar May 20 '24 21:05 kibertoad

@kibertoad Interesting! That's my something new learnt today, thank you

nickramsbottom avatar May 20 '24 21:05 nickramsbottom