import { type INestApplication } from "@nestjs/common"; import { ConfigService } from "@nestjs/config"; import { NestFactory } from "@nestjs/core"; import { Logger } from "nestjs-pino"; import helmet from "helmet"; import cookieParser from "cookie-parser"; import * as express from "express"; import type { CookieOptions, Response, NextFunction, Request } from "express"; /* eslint-disable @typescript-eslint/no-namespace */ declare global { namespace Express { interface Response { setSecureCookie: (name: string, value: string, options?: CookieOptions) => void; } } } /* eslint-enable @typescript-eslint/no-namespace */ import { UnifiedExceptionFilter } from "../core/http/exception.filter"; import { AppModule } from "../app.module"; export async function bootstrap(): Promise { const app = await NestFactory.create(AppModule, { bufferLogs: true, // bodyParser is enabled by default in NestJS rawBody: true, // Enable raw body access for debugging }); // 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 const expressInstance = app.getHttpAdapter().getInstance() as { disable?: (name: string) => void; }; if (typeof expressInstance?.disable === "function") { expressInstance.disable("x-powered-by"); } // Configure JSON body parser with proper limits app.use(express.json({ limit: "10mb" })); app.use(express.urlencoded({ extended: true, limit: "10mb" })); // Enhanced cookie parser with security options app.use(cookieParser()); // Provide helper for secure cookie handling without mutating Express response methods const secureCookieDefaults: CookieOptions = { httpOnly: true, sameSite: "strict", secure: configService.get("NODE_ENV") === "production", }; app.use((_req: Request, res: Response, next: NextFunction) => { res.setSecureCookie = (name: string, value: string, options: CookieOptions = {}) => { res.cookie(name, value, { ...secureCookieDefaults, ...options }); }; next(); }); // 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", "X-CSRF-Token", ], exposedHeaders: ["X-Total-Count", "X-Page-Count"], maxAge: 86400, // 24 hours }); // Global exception filter - single unified filter for all errors app.useGlobalFilters(new UnifiedExceptionFilter(app.get(Logger), app.get(ConfigService))); // Global authentication guard will be registered via APP_GUARD provider in AuthModule // Rely on Nest's built-in shutdown hooks. External orchestrator will send signals. app.enableShutdownHooks(); // 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, "0.0.0.0"); // Enhanced startup information logger.log(`🚀 BFF API running on: http://localhost:${port}/api`); logger.log(`🌐 Frontend Portal: http://localhost:${configService.get("NEXT_PORT", 3000)}`); if (configService.get("DATABASE_URL")) { logger.log("🗄️ Database connection configured"); } logger.log(`🔗 Prisma Studio: http://localhost:5555`); if (configService.get("REDIS_URL")) { logger.log("🔴 Redis connection configured"); } return app; }