import 'reflect-metadata'; import { VersioningType } from '@nestjs/common'; import { NestFactory } from '@nestjs/core'; import type { NestExpressApplication } from '@nestjs/platform-express'; import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'; import compression from 'compression'; import cookieParser from 'cookie-parser'; import helmet from 'helmet'; import { Logger } from 'nestjs-pino'; import { AppModule } from './app.module'; import { CURRENT_API_VERSION } from './common/constants/api'; import { requestIdMiddleware } from './common/middleware/request-id.middleware'; import { APP_CONFIG } from './config/app-config.module'; import type { AppConfig } from './config/configuration'; async function bootstrap(): Promise { const app = await NestFactory.create(AppModule, { // Buffer startup logs until the pino logger is attached, so boot output is // structured too rather than a mix of two formats. bufferLogs: true, }); const config = app.get(APP_CONFIG); app.useLogger(app.get(Logger)); app.flushLogs(); // Behind Nginx: required for correct client IPs in rate limiting and logs. app.set('trust proxy', 1); // First in the chain: every log line and error response carries this id. app.use(requestIdMiddleware); // Refresh tokens arrive as httpOnly cookies; without this `req.cookies` is // undefined and every refresh silently fails as "no session". app.use(cookieParser()); app.use(helmet({ crossOriginResourcePolicy: { policy: 'cross-origin' } })); app.use(compression()); app.enableCors({ origin: [...config.app.corsOrigins], credentials: true, exposedHeaders: ['x-request-id'], }); app.setGlobalPrefix(config.app.globalPrefix); /** * URI versioning: /api/v1/products. * * Chosen over headers because it is visible in logs, cacheable by CDN path, * trivially testable with curl, and unambiguous for the mobile app and * partner integrations that will follow. See ADR-0005. */ app.enableVersioning({ type: VersioningType.URI, defaultVersion: CURRENT_API_VERSION, }); app.enableShutdownHooks(); if (!config.app.isProduction) { const swaggerConfig = new DocumentBuilder() .setTitle('Sport Store API') .setDescription('REST API for the storefront and admin dashboard.') .setVersion(config.app.version) .addBearerAuth({ type: 'http', scheme: 'bearer', bearerFormat: 'JWT' }) .build(); SwaggerModule.setup('docs', app, SwaggerModule.createDocument(app, swaggerConfig), { jsonDocumentUrl: 'docs/json', }); } await app.listen(config.app.port, '0.0.0.0'); } void bootstrap();