import { NestFactory } from "@nestjs/core"; import { ValidationPipe } from "@nestjs/common"; import { SwaggerModule, DocumentBuilder } from "@nestjs/swagger"; import { ConfigService } from "@nestjs/config"; import { Logger } from "nestjs-pino"; import helmet from "helmet"; import cookieParser from "cookie-parser"; import { AppModule } from "./app.module"; import { GlobalExceptionFilter } from "./common/filters/http-exception.filter"; async function bootstrap() { const app = await NestFactory.create(AppModule, { bufferLogs: true }); // Set Pino as the logger app.useLogger(app.get(Logger)); const configService = app.get(ConfigService); const logger = app.get(Logger); // Enhanced Security Headers app.use( helmet({ contentSecurityPolicy: { directives: { defaultSrc: ["'self'"], styleSrc: ["'self'", "'unsafe-inline'"], scriptSrc: ["'self'"], imgSrc: ["'self'", "data:", "https:"], connectSrc: ["'self'"], fontSrc: ["'self'"], objectSrc: ["'none'"], mediaSrc: ["'self'"], frameSrc: ["'none'"], }, }, crossOriginEmbedderPolicy: false, crossOriginResourcePolicy: { policy: "cross-origin" }, }) ); // Disable x-powered-by header app.getHttpAdapter().getInstance().disable("x-powered-by"); // Enhanced cookie parser with security options app.use(cookieParser()); // Trust proxy configuration for reverse proxies if (configService.get("TRUST_PROXY", "false") === "true") { const httpAdapter = app.getHttpAdapter(); const instance = httpAdapter.getInstance() as { set?: (key: string, value: unknown) => void; }; if (typeof instance?.set === "function") { instance.set("trust proxy", 1); } } // Enhanced CORS configuration const corsOrigin = configService.get("CORS_ORIGIN"); app.enableCors({ origin: corsOrigin ? [corsOrigin] : false, credentials: true, methods: ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"], allowedHeaders: [ "Origin", "X-Requested-With", "Content-Type", "Accept", "Authorization", "X-API-Key", ], exposedHeaders: ["X-Total-Count", "X-Page-Count"], maxAge: 86400, // 24 hours }); // Global validation pipe with enhanced security app.useGlobalPipes( new ValidationPipe({ transform: true, whitelist: true, forbidNonWhitelisted: true, forbidUnknownValues: true, disableErrorMessages: configService.get("NODE_ENV") === "production", validationError: { target: false, value: false, }, }) ); // Global exception filter app.useGlobalFilters(new GlobalExceptionFilter(app.get(Logger))); // Rely on Nest's built-in shutdown hooks. External orchestrator will send signals. app.enableShutdownHooks(); // Swagger documentation (only in non-production) - SETUP BEFORE GLOBAL PREFIX if (configService.get("NODE_ENV") !== "production") { const config = new DocumentBuilder() .setTitle("Customer Portal API") .setDescription("Backend for Frontend API for customer portal") .setVersion("1.0") .addBearerAuth() .addCookieAuth("auth-cookie") .addServer("http://localhost:4000", "Development server") .addServer("https://api.yourdomain.com", "Production server") .build(); const document = SwaggerModule.createDocument(app, config); SwaggerModule.setup("docs", app, document); } // API routing prefix is applied via RouterModule in AppModule for clarity and modern routing. const port = Number(configService.get("BFF_PORT", 4000)); await app.listen(port); // Enhanced startup information logger.log(`🚀 BFF API running on: http://localhost:${port}/api`); logger.log(`🌐 Frontend Portal: http://localhost:${configService.get("NEXT_PORT", 3000)}`); logger.log( `🗄️ Database: ${configService.get("DATABASE_URL", "postgresql://dev:dev@localhost:5432/portal_dev")}` ); logger.log(`🔴 Redis: ${configService.get("REDIS_URL", "redis://localhost:6379")}`); if (configService.get("NODE_ENV") !== "production") { logger.log(`📚 API Documentation: http://localhost:${port}/docs`); } } void bootstrap();