managarten/packages/shared-nestjs-setup/src/index.ts
Till-JS fbd315eac0 🔧 chore: create @manacore/shared-nestjs-setup and migrate 8 backends
- Create shared package with bootstrapApp(), configureCors(), configureValidation()
- Migrate: chat, calendar, contacts, zitare, clock, planta, presi, nutriphi
- Skip complex backends: manadeck, picture, todo, skilltree, questions, storage

Savings: ~280 LOC (8 backends × 35 LOC each)
2026-01-29 17:25:51 +01:00

137 lines
3.5 KiB
TypeScript

/**
* Shared NestJS Bootstrap Utilities for ManaCore Backends
*
* Provides a consistent setup for CORS, validation, and global prefix
* across all backend applications.
*/
import { INestApplication, ValidationPipe, Type } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
/**
* Default CORS origins for local development
* These ports cover most common dev scenarios
*/
export const DEFAULT_CORS_ORIGINS = [
'http://localhost:3000', // Common web dev port
'http://localhost:3001', // Mana-core-auth
'http://localhost:5173', // Vite default
'http://localhost:8081', // Expo
'exp://localhost:8081', // Expo native
];
/**
* Configuration options for the bootstrap utility
*/
export interface BootstrapOptions {
/** Default port if PORT env is not set */
defaultPort: number;
/** Service name for console log message */
serviceName: string;
/** Additional CORS origins beyond defaults (app-specific web port) */
additionalCorsOrigins?: string[];
/** API prefix (default: 'api/v1') */
apiPrefix?: string;
/** Routes to exclude from global prefix (default: ['metrics', 'health']) */
excludeFromPrefix?: string[];
/** HTTP methods for CORS (default: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS']) */
corsMethods?: string[];
}
/**
* Parse CORS origins from environment or use defaults
*/
function getCorsOrigins(additionalOrigins: string[] = []): string[] {
const envOrigins = process.env.CORS_ORIGINS?.split(',').map((origin) => origin.trim());
if (envOrigins && envOrigins.length > 0) {
return envOrigins;
}
return [...DEFAULT_CORS_ORIGINS, ...additionalOrigins];
}
/**
* Configure standard CORS settings for the application
*/
export function configureCors(
app: INestApplication,
options: { additionalOrigins?: string[]; methods?: string[] } = {}
): void {
const corsOrigins = getCorsOrigins(options.additionalOrigins);
const methods = options.methods || ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'];
app.enableCors({
origin: corsOrigins,
methods,
credentials: true,
});
}
/**
* Configure standard validation pipe settings
*/
export function configureValidation(app: INestApplication): void {
app.useGlobalPipes(
new ValidationPipe({
whitelist: true,
transform: true,
forbidNonWhitelisted: true,
})
);
}
/**
* Configure global API prefix with standard exclusions
*/
export function configurePrefix(
app: INestApplication,
prefix = 'api/v1',
exclude: string[] = ['metrics', 'health']
): void {
app.setGlobalPrefix(prefix, { exclude });
}
/**
* Bootstrap a NestJS application with standard configuration
*
* @example
* ```typescript
* import { bootstrapApp } from '@manacore/shared-nestjs-setup';
* import { AppModule } from './app.module';
*
* bootstrapApp(AppModule, {
* defaultPort: 3002,
* serviceName: 'Chat',
* additionalCorsOrigins: ['http://localhost:5178'],
* });
* ```
*/
export async function bootstrapApp(
AppModule: Type<unknown>,
options: BootstrapOptions
): Promise<INestApplication> {
const app = await NestFactory.create(AppModule);
// Configure CORS
configureCors(app, {
additionalOrigins: options.additionalCorsOrigins,
methods: options.corsMethods,
});
// Configure validation
configureValidation(app);
// Configure global prefix
configurePrefix(
app,
options.apiPrefix ?? 'api/v1',
options.excludeFromPrefix ?? ['metrics', 'health']
);
// Start listening
const port = process.env.PORT || options.defaultPort;
await app.listen(port);
console.log(`${options.serviceName} backend running on http://localhost:${port}`);
return app;
}