80 lines
2.6 KiB
TypeScript
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();
|