Fastify Error Handlers

Fastify Error Handlers

Understand how Fastify manage your application errors

ยท

4 min read

by Manuel Spigolon

All you need to know to take control of the Fastify errors!

Error types

Errors in the Fastify world could be grouped in:

  1. Startup errors are triggered when the application starts and the server won't start
  2. Runtime errors happen when the server receives an HTTP call and the server will remain up&running:
    1. Application errors are throws by the developer when the business logic need it
    2. Unexpected errors are throws when there is a bug
    3. Validation errors appear when the submitted data from a client doesn't match the endpoint's JSON schema
    4. 404 errors when the requested route doesn't exist

Manage Runtime errors

Let's deep dive into the most interesting kinds of errors: handler's error. During your application lifecycle, it is necessary to validate user input, check data consistency, and so on.

So manage errors is a key feature that Fastify support through these options:

As we see, Fastify has a lot of tools that can work together to archive all your need to reply with clear errors!

The first aspect to explain is the difference between:

  • throwing an Error: this happens when an Error instance is processed
  • sending a JSON error: this happens when an HTTP status code >= 300 is set and a JSON is processed
  • unexpected exception: this happens due to a nasty bug, don't worry, Fastify will handle it for you!

Here is a code example in sync and async handler:


function callbackStyleHandler (request, reply) {
  // "throwing" an error
  reply.send(new Error('foo bar error'))
  // ...or sending a json error
  reply.code(500).send({ message: 'foo bar error' })
  // ...or unexpected exception
  'this is not an array'.sort() 
  // fastify will handle the TypeError for you
}

async function asyncHandler (request, reply) {
  // "throwing" an error
  throw new Error('foo bar error')
  // ...or sending a json error
  reply.code(500)
  return { message: 'foo bar error' }
  // ...or unexpected exception
  'this is not an array'.sort()
  // fastify will handle the TypeError for you
}

So, based on what you are sending (in sync handlers) or returning (in async handler), the send lifecycle will act like this:

Fastify error lifecycle

So, sending a JSON error will not execute the error handler and the onError hooks too. What your functions return may impact the execution of your code!

Every component of this flow can be customized for every route!! Thanks to all the route options you can add some route customization when needed that will overwrite the default setting in the fastify instance.

Notice that in async handler returns an Error or throws it is the same:

throw new Error('foo bar error')
// it is like
return new Error('foo bar error')

_You can find a complete code example that replicates that reply.send flow at github.com/Eomm/fastify-discord-bot-demo

Manage Startup errors

These kinds of errors are the most common at the beginning of a new application. They can be triggered by:

  • plugins that don't start due to an error, like a failed DB connection
  • plugins that don't start in time, like a pre-fetch to a slow endpoint
  • bad usages of the Fastify framework, like defining 2 routes with the same path

To manage these errors you have to check the listen or the ready results:

fastify.register((instance, ops, next) => {
  next(new Error('this plugin failed to load'))
})

fastify.listen(8080, (err) => {
  if (err) {
    // startup error
    fastify.log.fatal(err)
    process.exit(1)
  }
})

Instead, if you want to ignore the error throws by one plugin (it should not, but Fastify lets you free to do what you want with your application) you can manage it like this and the server will start as expected.

fastify.register((instance, ops, next) => {
  next(new Error('this plugin failed to load'))
}).after(err => {
  fastify.log.warn(err, 'Ops, my plugin fail to load, but nevermind')
})

Now, let's assume that the plugin may throw two errors: one you can ignore and one cannot be ignored:

fastify.register((instance, ops, next) => {
  if (condition) {
    next(new Error('cannot be ignored'))
  } else {
    next(new Error('can be ignored'))
  }
}).after((err, instance, next) => {
  if (err.message === 'cannot be ignored') {
    next(err)
  } else {
    fastify.log.warn(err, 'Ops, my plugin fail to load, but nevermind')
    next()
  }
})

Timeout

As said, the plugin has a maximum amount of time to start correctly. To customize this timeout you can set the pluginTimeout option:

const fastify = Fastify({
  pluginTimeout: 100000, // millisec
  logger: true
})

End

Now, I hope I have been teaching you about all you need to know to manage the application errors in your Fastify server! For more Fastify content follow me on twitter!

Write comments here below or open an issue on GitHub for any questions or feedback! Thank you for reading!

Acknowledgements

Thank you very much to Alona for the great feedback!

ย