@webxsid/nest-exception
v1.1.0
Published
A centralized exception handling module for NestJS applications. It provides structured error management, logging, and automatic exception handling.
Downloads
23
Maintainers
Readme
@webxsid/nest-exception
A centralized exception handling module for NestJS applications. It provides structured error management, logging, and automatic exception handling.
Features
- Centralized Error Registry: Define and manage application errors easily.
- Automatic Error Handling: Custom
AppExceptionclass integrates seamlessly. - Flexible Error Registration: Predefine errors in the module or register dynamically.
- Extendable Error Handling: Customize error handling with
ExceptionFilter. - Stack Trace (Development Mode): Automatically captures stack trace for debugging.
- Seamless Integration: Just import the module and start using it.
Installation
Install the package using npm or yarn:
$ npm install @webxsid/nest-exception
# or
$ yarn add @webxsid/nest-exceptionUsage
Importing and Setting Up the Module
- Import the
AppExceptionModulein the root module usingforRootorforRootAsync:
import { Module } from '@nestjs/common';
import { AppExceptionModule } from '@webxsid/nest-exception';
@Module({
imports: [AppExceptionModule.forRoot({
isDev: process.env.NODE_ENV === 'development',
errors: [
{ code: 'E001', message: 'User not found' },
{ code: 'E002', message: 'Invalid credentials' },
],
logger: LoggerService // Any implementation of LoggerService
})],
})
export class AppModule {}Async Configuration
import { Module } from '@nestjs/common';
import { AppExceptionModule } from '@webxsid/nest-exception';
import { ConfigModule, ConfigService } from '@nestjs/config';
@Module({
imports: [
ConfigModule.forRoot(),
AppExceptionModule.forRootAsync({
imports: [ConfigModule],
useFactory: (configService: ConfigService) => ({
isDev: configService.get('NODE_ENV') === 'development',
errors: [
{ code: 'E001', message: 'User not found' },
{ code: 'E002', message: 'Invalid credentials' },
],
logger: LoggerService // Any implementation of LoggerService
}),
inject: [ConfigService]
})
],
})
export class AppModule {}Registering the Global Exception Filter
To apply the AppExceptionFilter globally in your application, register it in your root module (AppModule):
// app.module.ts
import { Module } from '@nestjs/common';
import { APP_FILTER } from '@nestjs/core';
import { AppExceptionFilter } from '@webxsid/nest-exception';
@Module({
providers: [
{
provide: APP_FILTER,
useClass: AppExceptionFilter,
},
],
})
export class AppModule {}Error Management
Registering Errors in the Module
Errors can be pre-registered in the module configuration:
imports: [
AppExceptionModule.forRoot({
errors: [
{ code: 'E001', message: 'User not found' },
{ code: 'E002', message: 'Invalid credentials' },
]
})
]Registering Errors Dynamically
- Use the
ExceptionRegistryservice to register errors at runtime:
import { Injectable } from '@nestjs/common';
import { ExceptionRegistry } from '@webxsid/nest-exception';
@Injectable()
export class AppService {
constructor(private readonly exceptionRegistry: ExceptionRegistry) {}
registerErrors() {
this.exceptionRegistry.registerError({ code: 'E003', message: 'Invalid request' });
}
}Extending the Exception Handler
The ExceptionHandlerService allows you to customize how specific exceptions are handled.
It supports both constructor-based and predicate-based handlers to reliably handle
errors from first-party code as well as third-party libraries (e.g. Mongo / Mongoose).
Constructor-based handler
import { Injectable, OnModuleInit, HttpStatus, ArgumentsHost } from '@nestjs/common';
import { ExceptionHandlerService } from '@webxsid/nest-exception';
import { MongoError } from 'mongodb';
@Injectable()
export class MongoErrorHandler implements OnModuleInit {
constructor(
private readonly exceptionHandlerService: ExceptionHandlerService,
) {}
onModuleInit() {
this.exceptionHandlerService.register(
MongoError,
(exception: MongoError, host: ArgumentsHost) => {
const response = host.switchToHttp().getResponse();
response.status(HttpStatus.BAD_REQUEST).json({
statusCode: HttpStatus.BAD_REQUEST,
errorCode: 'MONGO_ERROR',
message: exception.message,
timestamp: new Date().toISOString(),
});
},
);
}
}Predicate-based handler (recommended for Mongo / Mongoose)
Some libraries throw plain objects instead of real Error instances. In these cases, constructor matching will not work.
Use registerWhen to match errors by shape or properties.
import { Injectable, OnModuleInit, HttpStatus, ArgumentsHost } from '@nestjs/common';
import { ExceptionHandlerService } from '@webxsid/nest-exception';
@Injectable()
export class MongoErrorHandler implements OnModuleInit {
constructor(
private readonly exceptionHandlerService: ExceptionHandlerService,
) {}
onModuleInit() {
this.exceptionHandlerService.registerWhen(
(error: any) =>
error?.name === 'MongoServerError' ||
error?.code === 11000,
(exception, host: ArgumentsHost) => {
const response = host.switchToHttp().getResponse();
response.status(HttpStatus.BAD_REQUEST).json({
statusCode: HttpStatus.BAD_REQUEST,
errorCode: 'MONGO_ERROR',
message: exception.message ?? 'MongoDB error',
timestamp: new Date().toISOString(),
});
},
);
}
}Handler resolution order
When an exception is caught, handlers are resolved in the following order: 1. Constructor-based handlers (including inheritance via the prototype chain) 2. Predicate-based handlers (canActivate fallback) 3. Global exception filter handling (default behavior)
This ensures correct polymorphic behavior while remaining resilient to wrapped or serialized errors.
Registering the handler
Add the handler class to your module providers:
@Module({
imports: [AppExceptionModule.forRoot(/*...*/)],
providers: [MongoErrorHandler]
})
export class AppModule {}Best practices
- Use constructor-based handlers for application-defined exceptions
- Use predicate-based handlers for third-party libraries
- Keep predicates specific to avoid accidental catch-all handlers
- Avoid relying solely on error names when possible
Throwing Custom Exceptions
- Use the
AppExceptionclass to throw predefined errors:
import { Injectable } from '@nestjs/common';
import { AppException } from '@webxsid/nest-exception';
@Injectable()
export class AppService {
async getUser(id: string) {
const user = await this.userService.findById(id);
if (!user) {
throw new AppException('E001');
}
return user;
}
}How It Works
The AppException class simplifies error handling by checking if the provided error code exists in the Exception Registry. Here’s how it behaves in different scenarios:
1. ✅ Passing a Registered Error Code
If the error code exists in the registry (either pre-registered in the module or added dynamically), AppException will: • Retrieve the corresponding error message and status code. • Construct a structured HTTP response with the correct status, message, and code.
throw new AppException('E001'); Output:
{
"statusCode": 400,
"errorCode": "E001",
"message": "User not found",
"timestamp": "2021-09-01T12:00:00.000Z"
}(Assuming the error code 'E001' is registered with the message 'User not found' and status code 400)
2. ❌ Passing an Unregistered Error Code or String
If the error code is not found in the registry, AppException will: • Throw an internal server error with the default message and status code. • Log the error using the provided logger service.
throw new AppException('Something went wrong'); Output:
{
"statusCode": 500,
"errorCode": "UNKNOWN_ERROR",
"message": "Internal server error",
"timestamp": "2021-09-01T12:00:00.000Z"
}🛠️ Development Mode (Stack Trace)
If development mode (isDev: true) is enabled, AppException will also include a stack trace for easier debugging:
{
"statusCode": 500,
"errorCode": "UNKNOWN_ERROR",
"message": "Internal server error",
"timestamp": "2021-09-01T12:00:00.000Z",
"stack": "Error: Internal server error\n at AppService.getUser (/app/app.service.ts:12:13)\n at processTicksAndRejections (internal/process/task_queues.js:93:5)"
}License
This project is licensed under the MIT License - see the LICENSE file for details.
