Skip to content

add error codes#911

Closed
StarpTech wants to merge 17 commits intofastify:masterfrom
StarpTech:feature/#909_error_codes
Closed

add error codes#911
StarpTech wants to merge 17 commits intofastify:masterfrom
StarpTech:feature/#909_error_codes

Conversation

@StarpTech
Copy link
Copy Markdown
Member

@StarpTech StarpTech commented Apr 27, 2018

This is a first draft of introducing error codes to fastify.

  • No performance degradation in success path
  • No performance degradation in error path

TODO

  • Move error message and code to error

Checklist

  • run npm run test and npm run benchmark
  • tests and/or benchmarks are included
  • documentation is changed or added
  • commit message and code follows Code of conduct

Comment thread lib/errors.js Outdated
@@ -0,0 +1,28 @@
'use strict'

const codes = {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

You mean native Errors objects? This is debatable. We already work with error objects and don't use inheritance.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

No, I mean FST_ERR_FOO should be equal to 42.

Copy link
Copy Markdown
Member Author

@StarpTech StarpTech Apr 27, 2018

Choose a reason for hiding this comment

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

From where do you get this information that an error code must be an integer? Node.js also using strings. Here sym is the code string and assigned as code property to the error object here

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Regardless, the issue is that strings are fragile. Symbols are fine.

Copy link
Copy Markdown
Member Author

@StarpTech StarpTech Apr 27, 2018

Choose a reason for hiding this comment

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

Yep, symbols are unique but can't be converting to strings (logging). Due to the fact that we prefix all strings we can guarantee their uniqueness.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think strings are fine, and what Node core is using. I’ll do a more detailed review asap.

Copy link
Copy Markdown
Member Author

@StarpTech StarpTech Apr 27, 2018

Choose a reason for hiding this comment

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

Mistake. Symbols are unique by default (at comparison) with a prefix we only guarantee collision-freedom (at definition). Do you think it will outperform the logging capabilities rather to write tests for that case?

Copy link
Copy Markdown
Member

@allevo allevo left a comment

Choose a reason for hiding this comment

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

Nice!

annotateError is used in twice way:

  • reply.send(annotateError(err, errorCodes.FST_ERR_ON_REQUEST_HOOK))
  • var err = new Error('Unsupported Media Type: ' + contentType); err = annotateError(err, errorCodes.FST_ERR_HTTP)

Can you rewrite using only one way?

@StarpTech
Copy link
Copy Markdown
Member Author

StarpTech commented Apr 29, 2018

Hi @allevo I'm using reply.send(annotateError(err, errorCodes.FST_ERR_ON_REQUEST_HOOK)) when there is no error to assign it's simpler and I can avoid a reassign like err = err.

@mcollina
Copy link
Copy Markdown
Member

I am not a fan of the approach of "annotating" extraneous errors with our codes. IMHO we should only be using codes for errors that we create. The code should be included in the error message, similarly to what Node.js does.

I'm actually ok in the new ContentLengthDiffersError() approach, as it would make our code more readable.

@allevo
Copy link
Copy Markdown
Member

allevo commented Apr 29, 2018

Another way could be createContentLengthDiffersError()

@delvedor
Copy link
Copy Markdown
Member

@delvedor delvedor added feature discussion Issues or PRs with this label will never stale labels Apr 30, 2018
@StarpTech
Copy link
Copy Markdown
Member Author

StarpTech commented Apr 30, 2018

I will try to meet all requirements:

  • Create own error objects
  • Code should be part of the error message
  • Don't modify extraneously errors

@StarpTech
Copy link
Copy Markdown
Member Author

Please check current state it's not final just an approach.

Comment thread lib/ContentTypeParser.js
var result = parser.fn(req, body, done)
if (result && typeof result.then === 'function') {
result.then(body => done(null, body)).catch(err => done(err))
result.then(body => done(null, body), done)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Errors thrown by the done function wont be caught anymore. Is this ok?

Copy link
Copy Markdown
Member

@delvedor delvedor May 2, 2018

Choose a reason for hiding this comment

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

When using promises you can catch errors in two ways:

promise.then(fn).catch(errorHandler)
//or
promise.then(fn, errorHandler)

So this is fine :)
https://developer.mozilla.org/it/docs/Web/JavaScript/Reference/Global_Objects/Promise/then

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I could be misunderstanding something but the first example will also catch the errors raised by fn whereas the second one wont. Right? It might be that done it is not going to raise any error, and in that case it would be fine.

Promise.resolve(1)
.then(() => { throw new Error('done() error') })
.catch(() => console.log('Caught!'))                    // Called

Promise.resolve(1)
.then(
  () => { throw new Error('done() error') },
  () => console.log('Caught!')                           // Never called
)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@dicearr is right in the theoretical side. However in this case this is not needed. done() is set up to be called in a callback-based scenario. In any case, we can just add in a little try-catch.
Avoiding the .catch()  saves us the allocation of one promise.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Sry, for the late response. What @mcollina said is correct and was my intention.

@StarpTech
Copy link
Copy Markdown
Member Author

@fastify/fastify please review the way of error handling when it's fine I can continue.

Copy link
Copy Markdown
Member

@mcollina mcollina left a comment

Choose a reason for hiding this comment

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

Good job! I think this is a semver-major change.

Comment thread lib/ContentTypeParser.js Outdated
if (parser === undefined) {
reply.code(415).send(new Error('Unsupported Media Type: ' + contentType))
var err = new FST_ERR_CTP_INVALID_MEDIA_TYPE(`Unsupported Media Type: ${contentType}`)
reply.code(415).send(err)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Should we include the code in the Error instead?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Example?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

err.core = 415

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

You mean err.statusCode = 415 ?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

yeah

Comment thread lib/errors.js
const errClass = class FastifyError extends Base {
constructor (...args) {
super(`Code: ${code}; ${createMessage(args)}`)
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

is this adding another level in the stacktrace? can you please verify and and add a test for that?

Copy link
Copy Markdown
Member Author

@StarpTech StarpTech May 6, 2018

Choose a reason for hiding this comment

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

No, the constructor call is omitted. Do you know how to test it reliable?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

@mcollina ping.

t.fail('should throw')
} catch (err) {
t.is(err.message, 'The body parser can only parse your data as \'string\' or \'buffer\', you asked \'fireworks\' which is not supported.')
t.is(err.message, `Code: FST_ERR_CTP_INVALID_PARSE_TYPE; The body parser can only parse your data as 'string' or 'buffer', you asked 'fireworks' which is not supported.`)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can you please follow the node core standard?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Could you make an example?

t.fail()
} catch (err) {
t.is(err.message, 'The content type should be a string')
t.is(err.message, 'Code: FST_ERR_CTP_INVALID_TYPE; The content type should be a string')
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

this should also test that the code is correct

Comment thread lib/ContentTypeParser.js Outdated
if (contentLength > limit) {
reply.code(413).send(new Error('Request body is too large'))
var err = new FST_ERR_CTP_BODY_TOO_LARGE('Request body is too large')
reply.code(413).send(err)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

here too

Comment thread lib/ContentTypeParser.js Outdated
req.removeListener('error', onEnd)
reply.code(413).send(new Error('Request body is too large'))
var err = new FST_ERR_CTP_BODY_TOO_LARGE('Request body is too large')
reply.code(413).send(err)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

here too

Comment thread lib/ContentTypeParser.js Outdated
if (!Number.isNaN(contentLength) && receivedLength !== contentLength) {
reply.code(400).send(new Error('Request body size did not match Content-Length'))
var contentLenghtErr = new FST_ERR_CTP_INVALID_CONTENT_LENGTH('Request body size did not match Content-Length')
reply.code(400).send(contentLenghtErr)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

here too

Comment thread lib/ContentTypeParser.js Outdated
var parser = this.cache.get(contentType) || this.getParser(contentType)
if (parser === undefined) {
reply.code(415).send(new Error('Unsupported Media Type: ' + contentType))
var err = new FST_ERR_CTP_INVALID_MEDIA_TYPE(`Unsupported Media Type: ${contentType}`)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think we should move the message to the errors file  as well.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

yes, would produce more readable code.

@StarpTech
Copy link
Copy Markdown
Member Author

StarpTech commented May 13, 2018

@mcollina @allevo Error message and statusCode was moved to errors file. In this way, we create specific fastify errors and pass only data to format the error message WDYT?

Copy link
Copy Markdown
Member

@allevo allevo left a comment

Choose a reason for hiding this comment

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

LGTM.

I don't like const errClass = class FastifyError extends Base {, but nice work!

@StarpTech
Copy link
Copy Markdown
Member Author

What you don't like exactly?

@allevo
Copy link
Copy Markdown
Member

allevo commented May 15, 2018

What you don't like exactly?

Creating different kind of classes at the start up it's strange, but cool: it's Javascript!

@StarpTech
Copy link
Copy Markdown
Member Author

StarpTech commented May 15, 2018

This approach is from node core but yes, it's a little getting used to.

Comment thread lib/errors.js Outdated
if (!msg) throw new Error('Fastify error message must not be empty')

code = code.toUpperCase()
const errClass = class FastifyError extends Base {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

can you avoid the const errClass = class FastifyError and just do codes[code] = FastifyError?

Copy link
Copy Markdown
Member Author

@StarpTech StarpTech May 17, 2018

Choose a reason for hiding this comment

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

What do you mean? Should I avoid classes?

@mcollina
Copy link
Copy Markdown
Member

Can you target this against a next branch, which will be v2?

@StarpTech
Copy link
Copy Markdown
Member Author

@mcollina
Copy link
Copy Markdown
Member

Create one forking from master.

@StarpTech StarpTech closed this Jun 2, 2018
@StarpTech StarpTech mentioned this pull request Jun 2, 2018
5 tasks
@github-actions
Copy link
Copy Markdown

This pull request has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

@github-actions github-actions Bot locked as resolved and limited conversation to collaborators Feb 17, 2022
@Eomm Eomm added the semver-minor Issue or PR that should land as semver minor label Apr 1, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

discussion Issues or PRs with this label will never stale semver-minor Issue or PR that should land as semver minor

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants