Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add isFastifyError #86

Closed
wants to merge 3 commits into from
Closed

add isFastifyError #86

wants to merge 3 commits into from

Conversation

Uzlopak
Copy link
Contributor

@Uzlopak Uzlopak commented Nov 2, 2022

Closes #17

Checklist

@Uzlopak Uzlopak requested a review from jsumners November 2, 2022 12:09
index.js Outdated Show resolved Hide resolved
@Uzlopak
Copy link
Contributor Author

Uzlopak commented Nov 2, 2022

@jsumners

Should i change

FastifyError.prototype[Symbol.toStringTag] = 'Error'

to

FastifyError.prototype[Symbol.toStringTag] = 'FastifyError'

?

@Uzlopak
Copy link
Contributor Author

Uzlopak commented Nov 2, 2022

I guess that is what you want.

Probably should be then a semver major.

@@ -36,7 +36,7 @@ function createError (code, message, statusCode = 500, Base = Error) {

this.statusCode = statusCode || undefined
}
FastifyError.prototype[Symbol.toStringTag] = 'Error'
FastifyError.prototype[Symbol.toStringTag] = 'FastifyError'
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potentially breaking change and should result in a major versoin bump

@Uzlopak Uzlopak requested review from Eomm and mcollina November 2, 2022 12:22
@@ -47,4 +47,11 @@ function createError (code, message, statusCode = 500, Base = Error) {
return FastifyError
}

function isFastifyError (value) {
return Object.prototype.toString.call(value) === '[object FastifyError]'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm hardly -1 on this approach.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mcollina I believe you want to say: "I'm strongly -1". Saying "I'm hardly" means "I'm in favor of this".

What is your disagreement with this? An instanceof check would break if someone is using their own import of fastify-error to check an error generated by Fastify's internal import of fastify-error.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mcollina @jsumners suggested this approach. What should we agree on?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it work with a custom symbol?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, the toStringTag is a spec defined symbol.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can just set our custom
Symbol for every instance created by the constructor and check for it. We already do this in our codebase.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True. But we are delving deeper into hack territory.

@Uzlopak
Copy link
Contributor Author

Uzlopak commented Nov 2, 2022

What about the classic?

function isFastifyError (value) {
  return (
    typeof value === 'object' &&
    value !== null &&
    value.name === 'FastifyError'
  )
}

I mean... if somebody changes the name of the error it is basically saying it is not a FastifyError anymore. Like we change the name from Error to FastifyError to indicate we have our own Error Class.

@mcollina
Copy link
Member

mcollina commented Nov 2, 2022

Going back to the original issue: what problem are we trying to solve for here? What usage do we want? I do not see a good case to use that method that's not a code smell. At this point instanceof does not worsen the coupling.

The fact that the whole Fastify ecosystem could be built without this existing should tell something.

@jsumners
Copy link
Member

jsumners commented Nov 2, 2022

Going back to the original issue: what problem are we trying to solve for here?

🤷‍♂️ I only have the recommendation to prevent "not an instance of" errors.

@mcollina
Copy link
Member

mcollina commented Nov 2, 2022

My best recommendation would be to have a utility to check if it has a code and/or statusCode set.

@Uzlopak
Copy link
Contributor Author

Uzlopak commented Nov 2, 2022

The question is if we should instead improve the documentation what we consider best practise and about ducktyping FastifyError.

I could do that instead, if we agree on

@@ -47,4 +47,11 @@ function createError (code, message, statusCode = 500, Base = Error) {
return FastifyError
}

function isFastifyError (value) {
return Object.prototype.toString.call(value) === '[object FastifyError]'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it work with a custom symbol?

t.plan(8)

const NewError = createError('CODE', 'Not available')
t.equal(isFastifyError(NewError()), true)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we should consider the createError instances as "application error" instead of Fastify Error

@fox1t
Copy link
Member

fox1t commented Nov 2, 2022

This is easy as adding a custom unique symbol to every instance created by the constructor and then checking for it inside the isFasitfyError function.
@mcollina having this function will allow the users to easier check inside the error handlers if it is an application-level error or an unexpected unhandled error.

@Uzlopak
Copy link
Contributor Author

Uzlopak commented Nov 2, 2022

I guess you could do

const kFastifyError = Symbol.for('FastifyError')

And add it like we do with toStringTag.

@mcollina
Copy link
Member

mcollina commented Nov 2, 2022

@mcollina having this function will allow the users to easier check inside the error handlers if it is an application-level error or an unexpected unhandled error.

I typically use this module for my applications too.

The only way to know if an error comes from Fastify is to check the prefix of the code property.

@jsumners
Copy link
Member

jsumners commented Nov 2, 2022

@mcollina having this function will allow the users to easier check inside the error handlers if it is an application-level error or an unexpected unhandled error.

I typically use this module for my applications too.

The only way to know if an error comes from Fastify is to check the prefix of the code property.

So your objection is the object name?

@mcollina
Copy link
Member

mcollina commented Nov 2, 2022

My objection is on how this is supposed to be used. I would instanceof individual errors, but not a blanket check against all the errors this module can produce.

If somebody wants all Fastify core errors, they can do err.code?.indexOf('FST_') === 0.

@jsumners
Copy link
Member

jsumners commented Nov 2, 2022

I would instanceof individual errors

This is not reliable. See the attached example. Within it is an example/00_main/index.js that imports some dependent library example/01_dependency/ that looks like:

'use strict'

const VError = require('verror') 
const foo = require('foo')

try {
  foo()
} catch (error) {
  console.log(error instanceof VError)
}

const error = VError('not foo')
console.log(error instanceof VError)

This example will output the following:

$ node 00_main/index.js
false
true

Notice that both the "main" application and the dependency rely on the exact same version of verror. In order for instanceof to work in this case, the 01_dependency would need to export its instance of VError so that 00_main could use it.

If verror were to implement [Symbol.toStringTag], the proposal in this PR would fix the issue.

example.zip

@fox1t
Copy link
Member

fox1t commented Nov 3, 2022

My objection is on how this is supposed to be used. I would instanceof individual errors, but not a blanket check against all the errors this module can produce.

If somebody wants all Fastify core errors, they can do err.code?.indexOf('FST_') === 0.

I agree with this. This proposal is also about knowing if the error is a fastifyError, regardless if Fastify, a plugin, or user code threw it. You are right, though, when you say that it is achievable checking for code and statusCode. I was thinking about providing a utility that can lower the barrier for the developers. I saw that error handling is usually one of the worst parts of the applications, and one of the barriers is handling application errors vs. unexpected errors. Boom has isBoom to allow the users check the error inside errorHandlers and act accordingly.

Hope this makes sense.

@Uzlopak
Copy link
Contributor Author

Uzlopak commented Nov 3, 2022

Also dont forget jest. It messes also instanceof Error up.

Maybe we should find a solution based on Symbol.hasInstance?

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/hasInstance

@mcollina
Copy link
Member

mcollina commented Nov 4, 2022

Here is my taken on how I would implement this for Fastify:

if (err.code.indexOf('FST_') === 0) {
  // All Fastify errors go there.
}

@jsumners
Copy link
Member

jsumners commented Nov 5, 2022

How would that solve the generic usage case of this module? I think the module name is confusing the goal. As I understand the desire, it is to determine if an error instance has been created by this module. Not necessarily that an error instance is one from the Fastify framework.

@mcollina
Copy link
Member

mcollina commented Nov 5, 2022

  1. I think the name of the module is incorrect in that sense as it has nothing to do with Fastify, but rather it's something we maintain.

  2. I don't see the reason to test if an error has been generated by this module. Why would you do it?

  3. I can see the reason to check if an error has code and statusCode properties set to meaningful values.

@fox1t
Copy link
Member

fox1t commented Nov 5, 2022

@mcollina,, checking if an error is a fastify error could allow the users to know if the error is an application logic error (fastifyError) or any other unhandled error. This information will enable the users to act accordingly: send a report to external tools, set proper code for the response, etc. Basically, the ordinary operations that we all usually do inside error handlers.

@Uzlopak
Copy link
Contributor Author

Uzlopak commented Nov 5, 2022

@fox1t
Repeating the same argument over and over does not make it more convincible.

@mcollina
Copy link
Member

mcollina commented Nov 5, 2022

@fox1t the solution described in #86 (comment) is perfect for that use case as it uses error codes.

@fox1t
Copy link
Member

fox1t commented Nov 5, 2022

@fox1t
Repeating the same argument over and over does not make it more convincible.

Pardon me? I was making the same point @jsumners made since I had the same impression that not everyone was on the same page. I don't have to convince anyone. Since I proposed this in the first place I tried to make sure that everyone understands what I am proposing.
@mcollina I agree with you. Here we are discussing if shipping a helper function could help the newcomers understand how to use this library as a generic error library instead of giving the impression it can be used only for Fastift core or Fastify plugins ecosystem. Since the error format is the same as I used in Interference and since we discussed the error format together a while ago, I think that this library can provide value outside of the Fastify ecosystem.

Copy link
Contributor

@xtx1130 xtx1130 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think is a good case in https://github.com/hapijs/boom/blob/master/lib/index.js#L92-L108. Both using Symbol.hasInstance and special parameters to judge an instance

@@ -37,6 +39,26 @@ const CustomError = createError('ERROR_CODE', 'Hello %s')
console.log(new CustomError('world')) // error.message => 'Hello world'
```

#### isFastifyError

The module exports a isFastifyError-function that you can use to check if a value is of type FastifyError, it takes one parameters:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
The module exports a isFastifyError-function that you can use to check if a value is of type FastifyError, it takes one parameters:
The module exports a isFastifyError-function that you can use to check if a value is of type FastifyError, it takes one parameter:

@Uzlopak
Copy link
Contributor Author

Uzlopak commented Sep 1, 2023

Closing due to inactivity (lol)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

should this package expose FastifyError?
6 participants