Comprehensive error handling system with custom error handlers and built-in error types.
Set custom error handlers for application-wide error processing.
/**
* Set custom error handler for all unhandled errors
* @param handler - Error handling function
* @returns FastifyInstance for method chaining
*/
setErrorHandler(handler: (
error: FastifyError,
request: FastifyRequest,
reply: FastifyReply
) => void | Promise<void>): FastifyInstance;Usage Examples:
// Basic error handler
fastify.setErrorHandler((error, request, reply) => {
// Log error
request.log.error(error);
// Send appropriate response
if (error.statusCode) {
reply.status(error.statusCode).send({
error: error.name,
message: error.message
});
} else {
reply.status(500).send({
error: 'Internal Server Error',
message: 'Something went wrong'
});
}
});
// Async error handler with custom logic
fastify.setErrorHandler(async (error, request, reply) => {
// Record error in monitoring system
await errorTracker.recordError(error, {
requestId: request.id,
userId: request.user?.id,
route: request.routerPath
});
// Handle different error types
if (error.code === 'FST_ERR_VALIDATION') {
reply.status(400).send({
error: 'Validation Error',
details: error.validation
});
} else if (error.statusCode === 404) {
reply.status(404).send({
error: 'Not Found',
message: 'The requested resource was not found'
});
} else {
reply.status(500).send({
error: 'Internal Server Error',
message: process.env.NODE_ENV === 'production'
? 'Something went wrong'
: error.message
});
}
});Handle 404 errors with custom logic.
/**
* Set custom handler for 404 Not Found errors
* @param handler - Not found handling function
* @returns FastifyInstance for method chaining
*/
setNotFoundHandler(handler: (
request: FastifyRequest,
reply: FastifyReply
) => void | Promise<void>): FastifyInstance;
/**
* Set not found handler with options
* @param options - Handler configuration options
* @param handler - Not found handling function
* @returns FastifyInstance for method chaining
*/
setNotFoundHandler(
options: {
preValidation?: Function | Function[];
preHandler?: Function | Function[];
},
handler: (request: FastifyRequest, reply: FastifyReply) => void | Promise<void>
): FastifyInstance;Usage Examples:
// Basic 404 handler
fastify.setNotFoundHandler((request, reply) => {
reply.code(404).send({
error: 'Not Found',
message: `Route ${request.method} ${request.url} not found`,
statusCode: 404
});
});
// 404 handler with custom logic
fastify.setNotFoundHandler(async (request, reply) => {
// Log 404s for analytics
request.log.warn(`404: ${request.method} ${request.url}`);
// Try to suggest similar routes
const suggestions = await findSimilarRoutes(request.url);
reply.code(404).send({
error: 'Not Found',
message: `Route ${request.url} not found`,
suggestions: suggestions.length > 0 ? suggestions : undefined
});
});
// 404 handler with preprocessing
fastify.setNotFoundHandler({
preValidation: (request, reply, done) => {
// Rate limit 404 requests
const key = `404_${request.ip}`;
rateLimiter.check(key, (err, allowed) => {
if (!allowed) {
reply.code(429).send({ error: 'Too Many Requests' });
return;
}
done();
});
}
}, (request, reply) => {
reply.code(404).send({ error: 'Not Found' });
});Understanding Fastify's built-in error types and creating custom errors.
/**
* Base Fastify error interface
*/
interface FastifyError extends Error {
name: string;
message: string;
code: string;
statusCode?: number;
validation?: FastifySchemaValidationError[];
validationContext?: string;
}Common Error Codes:
// Built-in Fastify error codes
const errorCodes = {
FST_ERR_VALIDATION: 'Request validation failed',
FST_ERR_INVALID_URL: 'Invalid URL',
FST_ERR_ROUTE_ALREADY_EXISTS: 'Route already exists',
FST_ERR_PLUGIN_NOT_VALID: 'Plugin is not valid',
FST_ERR_HOOK_INVALID_TYPE: 'Hook type is invalid',
FST_ERR_HOOK_INVALID_HANDLER: 'Hook handler is invalid',
FST_ERR_INSTANCE_ALREADY_LISTENING: 'Instance is already listening',
// ... many more
};
// Access error codes
const { FST_ERR_VALIDATION } = require('fastify').errorCodes;Create and throw custom errors with proper status codes.
// Using @fastify/error for custom errors
const createError = require('@fastify/error');
// Define custom error types
const ValidationError = createError('VALIDATION_ERROR', 'Validation failed: %s', 400);
const AuthorizationError = createError('AUTHORIZATION_ERROR', 'Access denied: %s', 403);
const NotFoundError = createError('NOT_FOUND_ERROR', 'Resource not found: %s', 404);
// Use in route handlers
fastify.get('/users/:id', async (request, reply) => {
const user = await findUser(request.params.id);
if (!user) {
throw new NotFoundError('User not found');
}
return user;
});
// Use in hooks
fastify.addHook('preHandler', async (request, reply) => {
if (!request.user) {
throw new AuthorizationError('Authentication required');
}
if (!request.user.active) {
throw new AuthorizationError('Account is disabled');
}
});Error handlers can be scoped to specific plugin contexts.
// Global error handler
fastify.setErrorHandler((error, request, reply) => {
request.log.error(error, 'Global error handler');
reply.status(500).send({ error: 'Internal Server Error' });
});
// Plugin-scoped error handler
await fastify.register(async function (fastify) {
// This error handler only applies to routes in this plugin
fastify.setErrorHandler((error, request, reply) => {
request.log.error(error, 'Plugin error handler');
if (error.code === 'PLUGIN_SPECIFIC_ERROR') {
reply.status(422).send({ error: 'Plugin specific error' });
} else {
// Forward to parent error handler
throw error;
}
});
fastify.get('/plugin-route', handler);
});Handle schema validation errors specifically.
// Custom validation error handling
fastify.setErrorHandler((error, request, reply) => {
if (error.code === 'FST_ERR_VALIDATION') {
const validationErrors = error.validation.map(err => ({
field: err.instancePath || err.schemaPath,
message: err.message,
value: err.data
}));
reply.status(400).send({
error: 'Validation Error',
message: 'Request validation failed',
details: validationErrors
});
return;
}
// Handle other errors
reply.status(error.statusCode || 500).send({
error: error.name || 'Error',
message: error.message
});
});
// Using attachValidation for manual handling
fastify.post('/users', {
attachValidation: true,
schema: { body: userSchema }
}, async (request, reply) => {
if (request.validationError) {
// Custom validation error response
reply.status(400).send({
success: false,
error: 'Invalid user data',
fields: request.validationError.validation.map(err => ({
name: err.instancePath.replace('/', ''),
message: err.message
}))
});
return;
}
const user = await createUser(request.body);
reply.status(201).send({ success: true, user });
});Enhance error handling with context and structured logging.
// Enhanced error handler with context
fastify.setErrorHandler(async (error, request, reply) => {
const errorContext = {
requestId: request.id,
method: request.method,
url: request.url,
userAgent: request.headers['user-agent'],
ip: request.ip,
userId: request.user?.id,
timestamp: new Date().toISOString(),
stack: error.stack
};
// Structured error logging
request.log.error({
err: error,
context: errorContext
}, 'Request error occurred');
// Send to external error tracking
if (process.env.NODE_ENV === 'production') {
await errorTracker.capture(error, errorContext);
}
// Send appropriate response
const isDevelopment = process.env.NODE_ENV === 'development';
reply.status(error.statusCode || 500).send({
error: error.name || 'Error',
message: error.message,
...(isDevelopment && { stack: error.stack }),
requestId: request.id
});
});Handle framework-level errors that occur outside normal request processing.
// Framework error handler (set in options)
const fastify = require('fastify')({
frameworkErrors: (error, request, reply) => {
// Handle framework-level errors (e.g., malformed JSON)
if (error.type === 'entity.parse.failed') {
reply.status(400).send({
error: 'Bad Request',
message: 'Invalid JSON in request body'
});
} else {
reply.status(500).send({
error: 'Framework Error',
message: 'A framework-level error occurred'
});
}
}
});