Summary: As @Manuel Spigolon said:
How can I guarantee that? The framework does it
Now we can take Manuel's word for it (SPOILER ALERT: they are absolutely correct), or we can prove how this works by looking in the source code on GitHub.
The first thing to keep in mind is that arrays in JavaScript are remain ordered by the way objects are pushed into them, but don’t take my word for it. That is all explained here if you want to dive a little deeper into the evidence. If that was not true, everything below doesn't matter and you could just stop reading now.
How addHook
works
Now that we have established that arrays maintain their order, let look at how the addHook
code is executed. We can start by looking at the default export of fastify in the fastify.js
file located in the root directory. In this object if scoll down you'll see the addHook
property defined. When we look into the addHook
function implementation we can see that in that add hook call we are calling this[kHooks].add
.
When we go back to see what the kHooks
property is we see that it is a new Hooks()
. When we go to take a look at the add
method on the Hooks
object, we can see that it just validates the hook that is being add and then [push
es] it to the array property on the Hooks
object with the matching hook name. This shows that hooks will always be in the order which add was called for them.
How fastify.route
adds hooks
I hope you're following to this point because that only proves the order of the addHook
calls in the respective array
on the Hooks
object. The next question is how these interact with the calls of fastify.(get | post | route | ...)
functions. We can walk through the fastify.get
function, but they are all pretty much the same (you can do the same exercise with any of them). Looking at the get
function, we see that the implementation is just calling the router.prepareRoute
function. When you look into the prepareRoute
implementation, you see that this function returns a call to the route
function. In the route function there is a section where the hooks are set up. It looks like this:
for (const hook of lifecycleHooks) {
const toSet = this[kHooks][hook]
.concat(opts[hook] || [])
.map(h => h.bind(this))
context[hook] = toSet.length ? toSet : null
}
What this does is go through every lifecycle hook and turn it into a set of all the hooks from the Fastify instance (this
) and the hooks in the options (opts[hook]
) for that given hook and binds them to the fastify instance (this
). This shows that the hooks in the options for the routes are always added after the addHook
handlers.
How Fastify executes hooks
This is not everything we need though. Now we know the order in which the hooks are stored. But how exactly are they executed? For that we can look at the hookRunner
function in the hooks.js file. We see this function acts as a sort of recursive loop that continues running as long as the handlers do not error. It first creates a variable i
to keep track of the handler function it is currently on and then tries to execute it and increments the function tracker (i
).
If the handler fails (handleReject
), it runs a callback function and does not call the next
function to continue. If the handler succeeds (handleResolve
), it just runs the next
function to try the same process on the following handler (functions[i++]
) in the functions set.
Why does this matter
This proves that the hook handlers are called in the order that they were pushed into the ordered collection. In other words:
How can I guarantee that? The framework does it