Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Hooks

A hook is a function Fastify calls at a certain stage of a request's or application's life. Hooks replace the middleware of other frameworks: they check access, add headers, write logs.

Request stages

HookWhen it runsExample task
onRequestRight after the request arrivesStart a timer, check a token
preValidationBefore schema validationPrepare data
preHandlerBefore the route handlerCheck permissions
onSendBefore the reply is sentChange the reply, add a header
onResponseAfter the reply is sentRecord statistics

Application hooks: onReady — after all plugins have loaded, onClose — when the server stops.

onRequest

Add a hook to the src/plugins/timing.js plugin from the previous lesson:

// src/plugins/timing.js
import fp from 'fastify-plugin';
 
async function timing(app) {
  app.decorateRequest('startTime', 0);
 
  app.addHook('onRequest', async (request) => {
    request.startTime = Date.now();
  });
}
 
export default fp(timing);

Now every request has its start time in request.startTime.

onSend

The onSend hook gets a third argument — the ready reply body. Add it to the same plugin:

// src/plugins/timing.js
app.addHook('onSend', async (request, reply, payload) => {
  reply.header('x-response-time', `${Date.now() - request.startTime}ms`);
  return payload;
});

curl -i http://localhost:3000/posts shows the header x-response-time: 2ms. The hook must return payload — whatever it returns goes to the client.

A hook for one route

A hook can be attached to a single route instead of the whole application — in the route options:

// src/routes/posts.js
async function checkAdminHeader(request, reply) {
  if (request.headers['x-admin'] !== 'yes') {
    return reply.code(403).send({ error: 'Access denied' });
  }
}
 
app.delete('/posts/:id', { preHandler: checkAdminHeader }, async (request, reply) => {
  return reply.code(204).send();
});

DELETE /posts/1 without the x-admin: yes header returns 403, and the handler does not run. A real token check is shown in the authentication lesson.

onClose

onClose runs on app.close() — that is where resources are released:

// src/plugins/timing.js
app.addHook('onClose', async () => {
  app.log.info('Server is stopping');
});

When the server stops through app.close(), this line appears in the log. In the database lesson the connection is closed here.

Scope

A hook added in a plugin without fastify-plugin affects only that plugin's routes. This way you can turn on a check for one group of routes.

Common mistake

If a hook sent a reply with reply.send() but did not return reply, Fastify keeps going and calls the route handler. In an async hook write return reply.code(403).send(...), as in the example above, or throw an error with throw.

Thanks for reading!

Donate to the site — we work hard for you.