// MUST be the very first import — see apps/portal-bff/src/observability/tracing.ts // for the reasoning. Anything `import`ed above this line bypasses the // OpenTelemetry auto-instrumentations and is silently un-traced. import './observability/tracing'; import { ValidationPipe } from '@nestjs/common'; import { NestFactory } from '@nestjs/core'; import { Logger } from 'nestjs-pino'; import { AppModule } from './app/app.module'; import { assertDatabaseUrl } from './config/check-database-url'; // Fail fast on a malformed DATABASE_URL (most often a special char in // the password that needs URL-encoding) rather than letting Prisma // surface a cryptic "invalid connection string" error mid-request. assertDatabaseUrl(); async function bootstrap() { // `bufferLogs: true` holds early-bootstrap log lines until the // Pino-based Logger is wired in below, so we don't lose anything // emitted before `app.useLogger()`. const app = await NestFactory.create(AppModule, { bufferLogs: true }); app.useLogger(app.get(Logger)); // CORS — minimal dev-time allowlist. The SPA running on // http://localhost:4200 issues fetches to the BFF and must be // able to send the W3C `traceparent` (and `tracestate`) headers // that `@opentelemetry/instrumentation-fetch` injects, so the BFF // can pick up the parent span id and emit child spans on the same // trace. The full security-grade allowlist (per-environment // origins, credentials policy, helmet stack, etc.) lands with the // phase-2 security ADR — for now this is the minimum needed for // end-to-end tracing. app.enableCors({ origin: (process.env['CORS_ALLOWED_ORIGINS'] ?? 'http://localhost:4200') .split(',') .map((o) => o.trim()) .filter(Boolean), allowedHeaders: ['Content-Type', 'Accept', 'Authorization', 'traceparent', 'tracestate'], credentials: true, }); app.useGlobalPipes( new ValidationPipe({ whitelist: true, forbidNonWhitelisted: true, transform: true, }), ); // Phase-2 security ADRs will harden the above: helmet, real CORS // allowlist, cookie-session, CSRF protection, rate limiting, auth // guards, structured error filter. const globalPrefix = 'api'; app.setGlobalPrefix(globalPrefix); const port = process.env['PORT'] ?? 3000; await app.listen(port); app .get(Logger) .log(`Application is running on: http://localhost:${port}/${globalPrefix}`, 'Bootstrap'); } bootstrap();