add NestJS skeleton and GET /api/health-check.

Thin API bootstrap with config module and nullcart-style health route for Docker checks.
This commit is contained in:
2026-09-27 23:05:28 +02:00
parent aff4d2cc6f
commit a785ce775f
14 changed files with 4516 additions and 0 deletions
+1
View File
@@ -6,6 +6,7 @@
/backend/node_modules /backend/node_modules
/backend/dist /backend/dist
/backend/*.tsbuildinfo
/backend/storage /backend/storage
/frontend/node_modules /frontend/node_modules
View File
+8
View File
@@ -0,0 +1,8 @@
{
"$schema": "https://json.schemastore.org/nest-cli",
"collection": "@nestjs/schematics",
"sourceRoot": "src",
"compilerOptions": {
"deleteOutDir": true
}
}
+4353
View File
File diff suppressed because it is too large Load Diff
+28
View File
@@ -0,0 +1,28 @@
{
"name": "backend",
"version": "0.0.1",
"private": true,
"license": "UNLICENSED",
"scripts": {
"build": "nest build",
"start": "nest start",
"start:dev": "nest start --watch",
"start:prod": "node dist/main"
},
"dependencies": {
"@nestjs/common": "^11.0.1",
"@nestjs/config": "^4.0.3",
"@nestjs/core": "^11.0.1",
"@nestjs/platform-express": "^11.0.1",
"class-transformer": "^0.5.1",
"class-validator": "^0.15.1",
"reflect-metadata": "^0.2.2"
},
"devDependencies": {
"@nestjs/cli": "^11.0.24",
"@nestjs/schematics": "^11.0.0",
"@types/express": "^5.0.0",
"@types/node": "^22.10.7",
"typescript": "~6.0.2"
}
}
+15
View File
@@ -0,0 +1,15 @@
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import appConfig from './config';
import { HealthCheckModule } from './modules/healthCheck/HealthCheckModule';
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
load: [appConfig]
}),
HealthCheckModule
]
})
export class AppModule {}
+22
View File
@@ -0,0 +1,22 @@
import { registerAs } from '@nestjs/config';
import { AppConfig } from '../types/AppConfig';
import { NodeEnv } from '../types/NodeEnv';
const env = (key: string): string => process.env[key] || '';
const envInt = (key: string): number => parseInt(env(key), 10);
export const getAppConfig = (): AppConfig => {
const rawOrigins = env('CORS_ORIGINS');
return {
port: envInt('BACKEND_PORT'),
nodeEnv: env('NODE_ENV') as NodeEnv,
corsOrigins: rawOrigins
.split(',')
.map(o => o.trim())
.filter(Boolean)
};
};
export default registerAs('app', getAppConfig);
+30
View File
@@ -0,0 +1,30 @@
import { ValidationPipe } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { NestFactory } from '@nestjs/core';
import { AppModule } from './AppModule';
import { AppConfig } from './types/AppConfig';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
const configService = app.get(ConfigService);
const { port, corsOrigins } = configService.get('app') as AppConfig;
app.setGlobalPrefix('api');
app.useGlobalPipes(
new ValidationPipe({
transform: true,
transformOptions: { enableImplicitConversion: true }
})
);
app.enableCors({
origin: corsOrigins,
credentials: true
});
await app.listen(port);
}
void bootstrap();
@@ -0,0 +1,7 @@
import { Module } from '@nestjs/common';
import { HealthCheckController } from './controllers/HealthCheckController';
@Module({
controllers: [HealthCheckController]
})
export class HealthCheckModule {}
@@ -0,0 +1,10 @@
import { Controller, Get, HttpStatus, Res } from '@nestjs/common';
import type { Response } from 'express';
@Controller('/health-check')
export class HealthCheckController {
@Get('/')
getStatus(@Res() res: Response) {
return res.sendStatus(HttpStatus.OK);
}
}
+7
View File
@@ -0,0 +1,7 @@
import { NodeEnv } from './NodeEnv';
export type AppConfig = {
port: number;
nodeEnv: NodeEnv;
corsOrigins: string[];
};
+4
View File
@@ -0,0 +1,4 @@
export enum NodeEnv {
Development = 'development',
Production = 'production'
}
+4
View File
@@ -0,0 +1,4 @@
{
"extends": "./tsconfig.json",
"exclude": ["node_modules", "dist", "**/*spec.ts"]
}
+27
View File
@@ -0,0 +1,27 @@
{
"compilerOptions": {
"module": "nodenext",
"moduleResolution": "nodenext",
"resolvePackageJsonExports": true,
"esModuleInterop": true,
"isolatedModules": true,
"declaration": true,
"removeComments": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
"target": "ES2023",
"sourceMap": true,
"outDir": "./dist",
"rootDir": "./src",
"types": ["node"],
"incremental": true,
"skipLibCheck": true,
"strictNullChecks": true,
"strictPropertyInitialization": false,
"forceConsistentCasingInFileNames": true,
"noImplicitAny": false,
"strictBindCallApply": false,
"noFallthroughCasesInSwitch": false
}
}