Files
web_sport/apps/api/src/main.ts
T
2026-08-13 23:20:22 +07:00

80 lines
2.6 KiB
TypeScript

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<void> {
const app = await NestFactory.create<NestExpressApplication>(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<AppConfig>(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();