/** * NestJS Integration Example * * This file demonstrates how to integrate the error handling system * into a NestJS application. * * @example Integration Steps: * 1. Install the global exception filter * 2. Use custom error classes in your services/controllers * 3. Configure request ID generation (optional) */ import { NestFactory } from '@nestjs/core'; import { Module, Controller, Get, Injectable, Param } from '@nestjs/common'; import { APP_FILTER } from '@nestjs/core'; import { GlobalExceptionFilter, NotFoundError, ValidationError, UnauthorizedError, BadRequestError, } from '@erp-suite/core'; // ======================================== // Option 1: Global Filter in main.ts // ======================================== /** * Bootstrap function with global exception filter */ async function bootstrap() { const app = await NestFactory.create(AppModule); // Register global exception filter app.useGlobalFilters(new GlobalExceptionFilter()); // Enable CORS, validation, etc. app.enableCors(); await app.listen(3000); } // ======================================== // Option 2: Provider-based Registration // ======================================== /** * App module with provider-based filter registration * * This approach allows dependency injection into the filter */ @Module({ imports: [], controllers: [UsersController], providers: [ UsersService, // Register filter as a provider { provide: APP_FILTER, useClass: GlobalExceptionFilter, }, ], }) export class AppModule {} // ======================================== // Usage in Services // ======================================== interface User { id: string; email: string; name: string; } @Injectable() export class UsersService { private users: User[] = [ { id: '1', email: 'user1@example.com', name: 'User One' }, { id: '2', email: 'user2@example.com', name: 'User Two' }, ]; /** * Find user by ID - throws NotFoundError if not found */ async findById(id: string): Promise { const user = this.users.find(u => u.id === id); if (!user) { throw new NotFoundError('User not found', { userId: id }); } return user; } /** * Create user - throws ValidationError on invalid data */ async create(email: string, name: string): Promise { // Validate email if (!email || !email.includes('@')) { throw new ValidationError('Invalid email address', { field: 'email', value: email, }); } // Check for duplicate email const existing = this.users.find(u => u.email === email); if (existing) { throw new BadRequestError('Email already exists', { email, existingUserId: existing.id, }); } const user: User = { id: String(this.users.length + 1), email, name, }; this.users.push(user); return user; } /** * Verify user access - throws UnauthorizedError */ async verifyAccess(userId: string, token?: string): Promise { if (!token) { throw new UnauthorizedError('Access token required'); } // Token validation logic... if (token !== 'valid-token') { throw new UnauthorizedError('Invalid or expired token', { userId, }); } } } // ======================================== // Usage in Controllers // ======================================== @Controller('users') export class UsersController { constructor(private readonly usersService: UsersService) {} /** * GET /users/:id * * Returns 200 with user data or 404 if not found */ @Get(':id') async getUser(@Param('id') id: string): Promise { // Service throws NotFoundError which is automatically // caught by GlobalExceptionFilter and converted to proper response return this.usersService.findById(id); } /** * Example error responses: * * Success (200): * { * "id": "1", * "email": "user1@example.com", * "name": "User One" * } * * Not Found (404): * { * "statusCode": 404, * "error": "Not Found", * "message": "User not found", * "details": { "userId": "999" }, * "timestamp": "2025-12-12T10:30:00.000Z", * "path": "/users/999", * "requestId": "req-123-456" * } */ } // ======================================== // Request ID Middleware (Optional) // ======================================== import { Injectable, NestMiddleware } from '@nestjs/core'; import { Request, Response, NextFunction } from 'express'; import { randomUUID } from 'crypto'; /** * Middleware to generate request IDs * * Add this to your middleware chain to enable request tracking */ @Injectable() export class RequestIdMiddleware implements NestMiddleware { use(req: Request, res: Response, next: NextFunction) { // Use existing request ID or generate new one const requestId = (req.headers['x-request-id'] as string) || (req.headers['x-correlation-id'] as string) || randomUUID(); // Set in request headers for downstream access req.headers['x-request-id'] = requestId; // Include in response headers res.setHeader('X-Request-ID', requestId); next(); } } // Register in AppModule import { MiddlewareConsumer, NestModule } from '@nestjs/common'; @Module({ // ... module configuration }) export class AppModuleWithRequestId implements NestModule { configure(consumer: MiddlewareConsumer) { consumer .apply(RequestIdMiddleware) .forRoutes('*'); // Apply to all routes } } // ======================================== // Custom Error Examples // ======================================== /** * You can also create custom domain-specific errors */ import { BaseError } from '@erp-suite/core'; export class InsufficientBalanceError extends BaseError { readonly statusCode = 400; readonly error = 'Insufficient Balance'; constructor(required: number, available: number) { super('Insufficient balance for this operation', { required, available, deficit: required - available, }); } } // Usage in service @Injectable() export class PaymentService { async processPayment(userId: string, amount: number): Promise { const balance = await this.getBalance(userId); if (balance < amount) { throw new InsufficientBalanceError(amount, balance); } // Process payment... } private async getBalance(userId: string): Promise { // Mock implementation return 100; } }