or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

content-parsing.mddecoration.mderror-handling.mdhooks.mdindex.mdplugins.mdrouting.mdschema.mdserver-lifecycle.mdtesting.md
tile.json

hooks.mddocs/

Hook System

Lifecycle hooks for intercepting and modifying request/response processing at various stages.

Capabilities

Hook Registration

Add lifecycle hooks to intercept request/response processing.

/**
 * Add a lifecycle hook
 * @param hookName - Name of the lifecycle hook
 * @param handler - Hook handler function
 * @returns FastifyInstance for method chaining
 */
addHook(hookName: HookName, handler: HookHandler): FastifyInstance;
type HookName = 
  | 'onRequest'
  | 'preParsing' 
  | 'preValidation'
  | 'preHandler'
  | 'preSerialization'
  | 'onSend'
  | 'onResponse'
  | 'onTimeout'
  | 'onError'
  | 'onClose'
  | 'onReady'
  | 'onListen'
  | 'onRegister'
  | 'onRequestAbort'
  | 'preClose';

Request Lifecycle Hooks

onRequest Hook

First hook executed in the request lifecycle, before body parsing.

/**
 * Called at the beginning of request processing
 * @param request - Fastify request object (body is null)
 * @param reply - Fastify reply object
 * @param done - Callback function for completion
 */
type onRequestHookHandler = (
  request: FastifyRequest,
  reply: FastifyReply,
  done: (err?: Error) => void
) => void;

/**
 * Async version of onRequest hook
 */
type onRequestAsyncHookHandler = (
  request: FastifyRequest,
  reply: FastifyReply
) => Promise<void>;

Usage Examples:

// Callback style
fastify.addHook('onRequest', (request, reply, done) => {
  // Log incoming requests
  console.log(`${request.method} ${request.url}`);
  done();
});

// Async style
fastify.addHook('onRequest', async (request, reply) => {
  // Add request ID
  request.id = generateRequestId();
  
  // Check rate limiting
  const allowed = await checkRateLimit(request.ip);
  if (!allowed) {
    reply.code(429).send({ error: 'Too Many Requests' });
  }
});

preParsing Hook

Called before request body parsing, allows payload modification.

/**
 * Called before body parsing, can modify payload
 * @param request - Fastify request object
 * @param reply - Fastify reply object
 * @param payload - Request payload stream
 * @param done - Callback with optional payload modification
 */
type preParsingHookHandler = (
  request: FastifyRequest,
  reply: FastifyReply,
  payload: RequestPayload,
  done: (err?: Error, payload?: RequestPayload) => void
) => void;

/**
 * Async version of preParsing hook
 */
type preParsingAsyncHookHandler = (
  request: FastifyRequest,
  reply: FastifyReply,
  payload: RequestPayload
) => Promise<RequestPayload | undefined>;

Usage Examples:

// Callback style - modify payload
fastify.addHook('preParsing', (request, reply, payload, done) => {
  // Decompress gzipped payload
  if (request.headers['content-encoding'] === 'gzip') {
    const gunzip = zlib.createGunzip();
    done(null, payload.pipe(gunzip));
  } else {
    done();
  }
});

// Async style - payload logging
fastify.addHook('preParsing', async (request, reply, payload) => {
  if (request.headers['content-type']?.includes('application/json')) {
    console.log('Processing JSON payload');
  }
});

preValidation Hook

Called after body parsing but before schema validation.

/**
 * Called after parsing, before validation
 * @param request - Fastify request object (body is parsed)
 * @param reply - Fastify reply object
 * @param done - Callback function for completion
 */
type preValidationHookHandler = (
  request: FastifyRequest,
  reply: FastifyReply,
  done: (err?: Error) => void
) => void;

/**
 * Async version of preValidation hook
 */
type preValidationAsyncHookHandler = (
  request: FastifyRequest,
  reply: FastifyReply
) => Promise<void>;

Usage Examples:

// Input sanitization
fastify.addHook('preValidation', async (request, reply) => {
  if (request.body && typeof request.body === 'object') {
    // Sanitize string fields
    for (const [key, value] of Object.entries(request.body)) {
      if (typeof value === 'string') {
        request.body[key] = value.trim();
      }
    }
  }
});

// Authentication check
fastify.addHook('preValidation', async (request, reply) => {
  const token = request.headers.authorization?.replace('Bearer ', '');
  if (!token) {
    reply.code(401).send({ error: 'Missing token' });
    return;
  }
  
  try {
    const user = await verifyToken(token);
    request.user = user;
  } catch (err) {
    reply.code(401).send({ error: 'Invalid token' });
  }
});

preHandler Hook

Called after validation but before route handler execution.

/**
 * Called after validation, before handler
 * @param request - Fastify request object (validated)
 * @param reply - Fastify reply object
 * @param done - Callback function for completion
 */
type preHandlerHookHandler = (
  request: FastifyRequest,
  reply: FastifyReply,
  done: (err?: Error) => void
) => void;

/**
 * Async version of preHandler hook
 */
type preHandlerAsyncHookHandler = (
  request: FastifyRequest,
  reply: FastifyReply
) => Promise<void>;

Usage Examples:

// Authorization check
fastify.addHook('preHandler', async (request, reply) => {
  const requiredRole = request.routeConfig?.requiredRole;
  if (requiredRole && (!request.user || !request.user.roles.includes(requiredRole))) {
    reply.code(403).send({ error: 'Insufficient permissions' });
  }
});

// Request context setup
fastify.addHook('preHandler', async (request, reply) => {
  request.context = {
    requestId: request.id,
    userId: request.user?.id,
    timestamp: Date.now()
  };
});

Response Lifecycle Hooks

preSerialization Hook

Called before response serialization, can modify payload.

/**
 * Called before response serialization
 * @param request - Fastify request object
 * @param reply - Fastify reply object
 * @param payload - Response payload to be serialized
 * @param done - Callback with optional payload modification
 */
type preSerializationHookHandler = (
  request: FastifyRequest,
  reply: FastifyReply,
  payload: any,
  done: (err?: Error, payload?: any) => void
) => void;

/**
 * Async version of preSerialization hook
 */
type preSerializationAsyncHookHandler = (
  request: FastifyRequest,
  reply: FastifyReply,
  payload: any
) => Promise<any>;

Usage Examples:

// Add metadata to all responses
fastify.addHook('preSerialization', async (request, reply, payload) => {
  if (payload && typeof payload === 'object' && !Array.isArray(payload)) {
    return {
      ...payload,
      _metadata: {
        requestId: request.id,
        timestamp: new Date().toISOString(),
        version: '1.0'
      }
    };
  }
  return payload;
});

// Response filtering based on user permissions
fastify.addHook('preSerialization', async (request, reply, payload) => {
  if (request.user && payload?.sensitive_data) {
    if (!request.user.roles.includes('admin')) {
      delete payload.sensitive_data;
    }
  }
  return payload;
});

onSend Hook

Called before sending the response, can modify the serialized payload.

/**
 * Called before sending response
 * @param request - Fastify request object
 * @param reply - Fastify reply object  
 * @param payload - Serialized response payload
 * @param done - Callback with optional payload modification
 */
type onSendHookHandler = (
  request: FastifyRequest,
  reply: FastifyReply,
  payload: string | Buffer,
  done: (err?: Error, payload?: string | Buffer) => void
) => void;

/**
 * Async version of onSend hook
 */
type onSendAsyncHookHandler = (
  request: FastifyRequest,
  reply: FastifyReply,
  payload: string | Buffer
) => Promise<string | Buffer>;

Usage Examples:

// Compress responses
fastify.addHook('onSend', async (request, reply, payload) => {
  if (request.headers['accept-encoding']?.includes('gzip') && 
      payload.length > 1024) {
    reply.header('content-encoding', 'gzip');
    return zlib.gzipSync(payload);
  }
  return payload;
});

// Response logging
fastify.addHook('onSend', (request, reply, payload, done) => {
  console.log(`Response ${reply.statusCode} for ${request.method} ${request.url}`);
  done();
});

onResponse Hook

Called after the response is sent to the client.

/**
 * Called after response is sent
 * @param request - Fastify request object
 * @param reply - Fastify reply object
 * @param done - Callback function for completion
 */
type onResponseHookHandler = (
  request: FastifyRequest,
  reply: FastifyReply,
  done: (err?: Error) => void
) => void;

/**
 * Async version of onResponse hook
 */
type onResponseAsyncHookHandler = (
  request: FastifyRequest,
  reply: FastifyReply
) => Promise<void>;

Usage Examples:

// Access logging
fastify.addHook('onResponse', async (request, reply) => {
  const responseTime = Date.now() - request.startTime;
  console.log(`${request.method} ${request.url} ${reply.statusCode} ${responseTime}ms`);
});

// Metrics collection
fastify.addHook('onResponse', async (request, reply) => {
  metrics.incrementCounter('http_requests_total', {
    method: request.method,
    status_code: reply.statusCode,
    route: request.routerPath
  });
  
  metrics.recordHistogram('http_request_duration_seconds', 
    (Date.now() - request.startTime) / 1000
  );
});

Error and Timeout Hooks

onError Hook

Called when an error occurs during request processing.

/**
 * Called when an error occurs
 * @param request - Fastify request object
 * @param reply - Fastify reply object
 * @param error - Error that occurred
 * @param done - Callback function for completion
 */
type onErrorHookHandler = (
  request: FastifyRequest,
  reply: FastifyReply,
  error: FastifyError,
  done: (err?: Error) => void
) => void;

/**
 * Async version of onError hook
 */
type onErrorAsyncHookHandler = (
  request: FastifyRequest,
  reply: FastifyReply,
  error: FastifyError
) => Promise<void>;

Usage Examples:

// Error logging
fastify.addHook('onError', async (request, reply, error) => {
  console.error(`Error in ${request.method} ${request.url}:`, error);
  
  // Send error to monitoring service
  await errorTracker.recordError(error, {
    requestId: request.id,
    userId: request.user?.id,
    route: request.routerPath
  });
});

// Custom error responses
fastify.addHook('onError', (request, reply, error, done) => {
  if (error.code === 'FST_ERR_VALIDATION') {
    reply.code(400).send({
      error: 'Validation Error',
      details: error.validation
    });
  }
  done();
});

onTimeout Hook

Called when a request times out.

/**
 * Called when request times out
 * @param request - Fastify request object
 * @param reply - Fastify reply object
 * @param done - Callback function for completion
 */
type onTimeoutHookHandler = (
  request: FastifyRequest,
  reply: FastifyReply,
  done: (err?: Error) => void
) => void;

/**
 * Async version of onTimeout hook
 */
type onTimeoutAsyncHookHandler = (
  request: FastifyRequest,
  reply: FastifyReply
) => Promise<void>;

Usage Examples:

// Timeout logging
fastify.addHook('onTimeout', async (request, reply) => {
  console.warn(`Request timeout: ${request.method} ${request.url}`);
  
  // Log slow requests for optimization
  await logSlowRequest({
    method: request.method,
    url: request.url,
    params: request.params,
    query: request.query
  });
});

onRequestAbort Hook

Called when a client aborts the request.

/**
 * Called when request is aborted by client
 * @param request - Fastify request object
 * @param done - Callback function for completion
 */
type onRequestAbortHookHandler = (
  request: FastifyRequest,
  done: (err?: Error) => void
) => void;

/**
 * Async version of onRequestAbort hook
 */
type onRequestAbortAsyncHookHandler = (
  request: FastifyRequest
) => Promise<void>;

Usage Examples:

// Cleanup on abort
fastify.addHook('onRequestAbort', async (request) => {
  console.log(`Request aborted: ${request.method} ${request.url}`);
  
  // Cancel any ongoing operations
  if (request.operationId) {
    await cancelOperation(request.operationId);
  }
});

Application Lifecycle Hooks

onReady Hook

Called when the application is ready and all plugins are loaded.

/**
 * Called when application is ready
 * @param done - Callback function for completion
 */
type onReadyHookHandler = (done: (err?: Error) => void) => void;

/**
 * Async version of onReady hook
 */
type onReadyAsyncHookHandler = () => Promise<void>;

Usage Examples:

// Database initialization
fastify.addHook('onReady', async () => {
  await database.connect();
  console.log('Database connected');
});

// Health check setup
fastify.addHook('onReady', async () => {
  setInterval(async () => {
    await performHealthCheck();
  }, 30000);
});

onListen Hook

Called when the server starts listening.

/**
 * Called when server starts listening
 * @param done - Callback function for completion
 */
type onListenHookHandler = (done: (err?: Error) => void) => void;

/**
 * Async version of onListen hook
 */
type onListenAsyncHookHandler = () => Promise<void>;

Usage Examples:

// Startup notifications
fastify.addHook('onListen', async () => {
  console.log('Server is now listening');
  await notifyServiceRegistry();
});

onClose Hook

Called when the server is closing.

/**
 * Called when server is closing
 * @param instance - Fastify instance being closed
 * @param done - Callback function for completion
 */
type onCloseHookHandler = (
  instance: FastifyInstance,
  done: (err?: Error) => void
) => void;

/**
 * Async version of onClose hook
 */
type onCloseAsyncHookHandler = (instance: FastifyInstance) => Promise<void>;

Usage Examples:

// Cleanup resources
fastify.addHook('onClose', async (instance) => {
  await database.disconnect();
  await redis.quit();
  console.log('Resources cleaned up');
});

preClose Hook

Called before the server starts closing.

/**
 * Called before server starts closing
 * @param done - Callback function for completion
 */
type preCloseHookHandler = (done: (err?: Error) => void) => void;

/**
 * Async version of preClose hook
 */
type preCloseAsyncHookHandler = () => Promise<void>;

onRegister Hook

Called when a plugin is registered.

/**
 * Called when a plugin is registered
 * @param instance - FastifyInstance where plugin is being registered
 * @param done - Callback function for completion
 */
type onRegisterHookHandler = (
  instance: FastifyInstance,
  done: (err?: Error) => void
) => void;

Usage Examples:

// Track plugin registration
fastify.addHook('onRegister', (instance, done) => {
  console.log(`Plugin registered: ${instance.pluginName}`);
  done();
});