Eine gut strukturierte NestJS-Anwendung ist der Grundstein für langfristige Wartbarkeit, Skalierbarkeit und Teamproduktivität. Dieses Kapitel erklärt die standardmäßige Projektstruktur von NestJS und zeigt bewährte Praktiken für die Organisation größerer Anwendungen auf.
Wenn Sie ein neues NestJS-Projekt mit
nest new project-name erstellen, erhalten Sie eine
standardisierte Verzeichnisstruktur, die als solide Basis für die
meisten Anwendungen dient.
my-nestjs-app/
├── dist/ # Kompilierte JavaScript-Ausgabe
├── node_modules/ # Abhängigkeiten
├── src/ # Quellcode der Anwendung
│ ├── app.controller.ts # Root-Controller
│ ├── app.controller.spec.ts # Tests für Root-Controller
│ ├── app.module.ts # Root-Modul
│ ├── app.service.ts # Root-Service
│ ├── app.service.spec.ts # Tests für Root-Service
│ └── main.ts # Einstiegspunkt der Anwendung
├── test/ # End-to-End Tests
│ ├── app.e2e-spec.ts # E2E Test-Spezifikationen
│ └── jest-e2e.json # Jest-Konfiguration für E2E
├── .eslintrc.js # ESLint-Konfiguration
├── .gitignore # Git-Ignore-Datei
├── .prettierrc # Prettier-Konfiguration
├── nest-cli.json # NestJS CLI-Konfiguration
├── package.json # NPM-Package-Definition
├── README.md # Projekt-Dokumentation
├── tsconfig.build.json # TypeScript Build-Konfiguration
└── tsconfig.json # TypeScript-Konfiguration
dist/: Dieses Verzeichnis enthält die kompilierten JavaScript-Dateien, die aus dem TypeScript-Quellcode generiert werden. Es wird automatisch beim Build-Prozess erstellt und sollte nicht manuell bearbeitet werden. In Produktionsumgebungen wird dieses Verzeichnis bereitgestellt.
src/: Das Herzstück Ihrer Anwendung. Hier befindet
sich der gesamte Anwendungscode, organisiert in einer modularen
Struktur. Jede TypeScript-Datei in diesem Verzeichnis wird kompiliert
und zu dist/ ausgegeben.
test/: Speziell für End-to-End-Tests konzipiert. Diese Tests prüfen das Verhalten der gesamten Anwendung von außen und simulieren echte Benutzerinteraktionen mit der API.
Bei der Projekterstellung werden mehrere Dateien automatisch generiert, die jeweils spezifische Zwecke erfüllen:
main.ts ist der Einstiegspunkt der Anwendung. Diese Datei initialisiert die NestJS-Anwendung, konfiguriert globale Middleware und startet den HTTP-Server:
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(3000);
}
bootstrap();app.module.ts definiert das Root-Modul der Anwendung. Es orchestriert alle anderen Module und stellt die Grundkonfiguration bereit:
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
@Module({
imports: [],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}Das src/-Verzeichnis bildet den Kern jeder
NestJS-Anwendung und sollte methodisch organisiert werden, um
Wartbarkeit und Verständlichkeit zu gewährleisten.
main.ts - Application Bootstrap
Die main.ts-Datei ist mehr als nur ein Einstiegspunkt.
Sie konfiguriert die gesamte Anwendung und kann erweitert werden
für:
import { NestFactory } from '@nestjs/core';
import { ValidationPipe } from '@nestjs/common';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// Globale Pipes konfigurieren
app.useGlobalPipes(new ValidationPipe({
whitelist: true,
forbidNonWhitelisted: true,
transform: true,
}));
// CORS aktivieren
app.enableCors({
origin: process.env.FRONTEND_URL || 'http://localhost:3000',
credentials: true,
});
// Swagger-Dokumentation konfigurieren
const config = new DocumentBuilder()
.setTitle('My API')
.setDescription('API-Dokumentation')
.setVersion('1.0')
.addBearerAuth()
.build();
const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup('api', app, document);
// Global Prefix setzen
app.setGlobalPrefix('api/v1');
const port = process.env.PORT || 3000;
await app.listen(port);
console.log(`Anwendung läuft auf Port ${port}`);
}
bootstrap();app.module.ts - Root Module Organization
Das Root-Modul wird mit wachsender Anwendungskomplexität erweitert:
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { UsersModule } from './modules/users/users.module';
import { AuthModule } from './modules/auth/auth.module';
import { ProductsModule } from './modules/products/products.module';
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
envFilePath: '.env',
}),
TypeOrmModule.forRoot({
type: 'postgres',
host: process.env.DB_HOST,
port: parseInt(process.env.DB_PORT),
username: process.env.DB_USERNAME,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
autoLoadEntities: true,
synchronize: process.env.NODE_ENV !== 'production',
}),
UsersModule,
AuthModule,
ProductsModule,
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}Während kleine Anwendungen mit wenigen Dateien im
src/-Verzeichnis auskommen, erfordern größere Projekte eine
durchdachte Strukturierung:
src/
├── modules/ # Feature-Module
│ ├── users/ # User-Management
│ ├── auth/ # Authentifizierung
│ ├── products/ # Produkt-Verwaltung
│ └── orders/ # Bestellungen
├── shared/ # Geteilte Ressourcen
│ ├── guards/ # Globale Guards
│ ├── interceptors/ # Globale Interceptors
│ ├── pipes/ # Globale Pipes
│ ├── filters/ # Exception Filters
│ └── decorators/ # Custom Decorators
├── common/ # Gemeinsame Utilities
│ ├── interfaces/ # TypeScript Interfaces
│ ├── enums/ # Enumerationen
│ ├── constants/ # Anwendungskonstanten
│ └── utils/ # Hilfsfunktionen
├── config/ # Konfigurationsdateien
│ ├── database.config.ts # Datenbank-Konfiguration
│ ├── jwt.config.ts # JWT-Konfiguration
│ └── app.config.ts # Allgemeine App-Konfiguration
├── app.controller.ts
├── app.module.ts
├── app.service.ts
└── main.ts
NestJS-Projekte enthalten verschiedene Konfigurationsdateien, die das Verhalten der Anwendung und des Entwicklungsprozesses steuern.
tsconfig.json - Haupt-TypeScript-Konfiguration:
{
"compilerOptions": {
"module": "commonjs",
"declaration": true,
"removeComments": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
"target": "ES2021",
"sourceMap": true,
"outDir": "./dist",
"baseUrl": "./",
"incremental": true,
"skipLibCheck": true,
"strictNullChecks": false,
"noImplicitAny": false,
"strictBindCallApply": false,
"forceConsistentCasingInFileNames": false,
"noFallthroughCasesInSwitch": false,
"paths": {
"@/*": ["src/*"],
"@modules/*": ["src/modules/*"],
"@shared/*": ["src/shared/*"],
"@common/*": ["src/common/*"],
"@config/*": ["src/config/*"]
}
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}tsconfig.build.json - Spezifische Build-Konfiguration:
{
"extends": "./tsconfig.json",
"compilerOptions": {
"removeComments": true,
"declaration": false,
"sourceMap": false
},
"exclude": [
"node_modules",
"test",
"dist",
"**/*spec.ts",
"**/*test.ts"
]
}nest-cli.json steuert das Verhalten der NestJS CLI:
{
"$schema": "https://raw.githubusercontent.com/nestjs/schematics/master/src/collection.json",
"collection": "@nestjs/schematics",
"sourceRoot": "src",
"entryFile": "main",
"monorepo": false,
"deleteOutDir": true,
"compilerOptions": {
"deleteOutDir": true,
"builder": "webpack",
"typeCheck": true,
"assets": [
"**/*.hbs",
"**/*.json",
"**/*.md"
],
"watchAssets": true
},
"generateOptions": {
"spec": {
"environment": "node"
}
}
}Die package.json enthält wichtige Scripts für
Entwicklung und Deployment:
{
"scripts": {
"build": "nest build",
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
"start": "nest start",
"start:dev": "nest start --watch",
"start:debug": "nest start --debug --watch",
"start:prod": "node dist/main",
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
"test": "jest",
"test:watch": "jest --watch",
"test:cov": "jest --coverage",
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "jest --config ./test/jest-e2e.json",
"migration:generate": "typeorm migration:generate",
"migration:run": "typeorm migration:run",
"migration:revert": "typeorm migration:revert"
}
}NestJS wurde von Grund auf für TypeScript entwickelt und nutzt dessen fortgeschrittene Features für robuste Anwendungsarchitektur.
TypeScript-Dekoratoren sind zentral für NestJS und ermöglichen deklarative Programmierung:
// Klassen-Dekoratoren
@Controller('users')
@UseGuards(JwtAuthGuard)
export class UsersController {
// Method-Dekoratoren
@Get()
@UseInterceptors(LoggingInterceptor)
@ApiOperation({ summary: 'Alle Benutzer abrufen' })
async findAll(): Promise<User[]> {
return this.usersService.findAll();
}
// Parameter-Dekoratoren
@Post()
async create(
@Body() createUserDto: CreateUserDto,
@Req() request: Request,
@CurrentUser() user: User,
): Promise<User> {
return this.usersService.create(createUserDto);
}
}Generics für typisierte Services:
export abstract class BaseService<T, CreateDto, UpdateDto> {
constructor(private readonly repository: Repository<T>) {}
async findAll(): Promise<T[]> {
return this.repository.find();
}
async findOne(id: string): Promise<T> {
const entity = await this.repository.findOne({ where: { id } });
if (!entity) {
throw new NotFoundException(`Entity with ID ${id} not found`);
}
return entity;
}
async create(createDto: CreateDto): Promise<T> {
const entity = this.repository.create(createDto as any);
return this.repository.save(entity);
}
}
@Injectable()
export class UsersService extends BaseService<User, CreateUserDto, UpdateUserDto> {
constructor(@InjectRepository(User) userRepository: Repository<User>) {
super(userRepository);
}
}Union Types und Discriminated Unions:
// Event-System mit Discriminated Unions
type UserEvent =
| { type: 'USER_CREATED'; payload: { userId: string; email: string } }
| { type: 'USER_UPDATED'; payload: { userId: string; changes: Partial<User> } }
| { type: 'USER_DELETED'; payload: { userId: string } };
@Injectable()
export class EventService {
async handleUserEvent(event: UserEvent): Promise<void> {
switch (event.type) {
case 'USER_CREATED':
await this.sendWelcomeEmail(event.payload.email);
break;
case 'USER_UPDATED':
await this.logUserChanges(event.payload.userId, event.payload.changes);
break;
case 'USER_DELETED':
await this.cleanupUserData(event.payload.userId);
break;
}
}
}Moderne NestJS-Anwendungen nutzen das
@nestjs/config-Modul für typisierte Konfiguration:
// config/database.config.ts
import { registerAs } from '@nestjs/config';
export interface DatabaseConfig {
host: string;
port: number;
username: string;
password: string;
database: string;
synchronize: boolean;
}
export default registerAs('database', (): DatabaseConfig => ({
host: process.env.DB_HOST || 'localhost',
port: parseInt(process.env.DB_PORT) || 5432,
username: process.env.DB_USERNAME || 'postgres',
password: process.env.DB_PASSWORD || 'password',
database: process.env.DB_NAME || 'myapp',
synchronize: process.env.NODE_ENV !== 'production',
}));import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { DatabaseConfig } from '../config/database.config';
@Injectable()
export class AppService {
constructor(private configService: ConfigService) {}
getDatabaseUrl(): string {
const dbConfig = this.configService.get<DatabaseConfig>('database');
return `postgresql://${dbConfig.username}:${dbConfig.password}@${dbConfig.host}:${dbConfig.port}/${dbConfig.database}`;
}
}NestJS erweitert das TypeScript-Dekorator-System für umfangreiche Metadaten-Verarbeitung.
// decorators/current-user.decorator.ts
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
export const CurrentUser = createParamDecorator(
(data: string, ctx: ExecutionContext) => {
const request = ctx.switchToHttp().getRequest();
const user = request.user;
return data ? user?.[data] : user;
},
);
// decorators/roles.decorator.ts
import { SetMetadata } from '@nestjs/common';
export const ROLES_KEY = 'roles';
export const Roles = (...roles: string[]) => SetMetadata(ROLES_KEY, roles);
// Verwendung in Controllern
@Controller('admin')
export class AdminController {
@Get('users')
@Roles('admin', 'moderator')
@UseGuards(JwtAuthGuard, RolesGuard)
async getUsers(@CurrentUser('id') userId: string): Promise<User[]> {
return this.usersService.findAll();
}
}// guards/roles.guard.ts
@Injectable()
export class RolesGuard implements CanActivate {
constructor(private reflector: Reflector) {}
canActivate(context: ExecutionContext): boolean {
const requiredRoles = this.reflector.getAllAndOverride<string[]>(ROLES_KEY, [
context.getHandler(),
context.getClass(),
]);
if (!requiredRoles) {
return true;
}
const { user } = context.switchToHttp().getRequest();
return requiredRoles.some((role) => user.roles?.includes(role));
}
}Bei wachsender Komplexität sollte die Projektstruktur entsprechend skaliert und organisiert werden.
src/
├── modules/ # Feature-Module
│ ├── auth/ # Authentifizierung
│ │ ├── controllers/
│ │ │ ├── auth.controller.ts
│ │ │ └── auth.controller.spec.ts
│ │ ├── services/
│ │ │ ├── auth.service.ts
│ │ │ ├── auth.service.spec.ts
│ │ │ ├── jwt.service.ts
│ │ │ └── password.service.ts
│ │ ├── guards/
│ │ │ ├── jwt-auth.guard.ts
│ │ │ ├── local-auth.guard.ts
│ │ │ └── roles.guard.ts
│ │ ├── strategies/
│ │ │ ├── jwt.strategy.ts
│ │ │ └── local.strategy.ts
│ │ ├── dto/
│ │ │ ├── login.dto.ts
│ │ │ ├── register.dto.ts
│ │ │ └── change-password.dto.ts
│ │ ├── entities/
│ │ │ └── refresh-token.entity.ts
│ │ ├── interfaces/
│ │ │ └── jwt-payload.interface.ts
│ │ └── auth.module.ts
│ │
│ ├── users/ # Benutzerverwaltung
│ │ ├── controllers/
│ │ ├── services/
│ │ ├── dto/
│ │ ├── entities/
│ │ ├── repositories/
│ │ └── users.module.ts
│ │
│ ├── products/ # Produktmanagement
│ │ ├── controllers/
│ │ ├── services/
│ │ ├── dto/
│ │ ├── entities/
│ │ └── products.module.ts
│ │
│ └── orders/ # Bestellverwaltung
│ ├── controllers/
│ ├── services/
│ ├── dto/
│ ├── entities/
│ ├── processors/ # Queue-Verarbeitung
│ └── orders.module.ts
│
├── shared/ # Geteilte Funktionalität
│ ├── guards/
│ │ ├── throttle.guard.ts
│ │ └── api-key.guard.ts
│ ├── interceptors/
│ │ ├── logging.interceptor.ts
│ │ ├── transform.interceptor.ts
│ │ └── timeout.interceptor.ts
│ ├── pipes/
│ │ ├── validation.pipe.ts
│ │ └── parse-object-id.pipe.ts
│ ├── filters/
│ │ ├── http-exception.filter.ts
│ │ ├── validation.filter.ts
│ │ └── all-exceptions.filter.ts
│ ├── decorators/
│ │ ├── api-paginated-response.decorator.ts
│ │ ├── api-file.decorator.ts
│ │ └── skip-throttle.decorator.ts
│ ├── services/
│ │ ├── email.service.ts
│ │ ├── file-upload.service.ts
│ │ └── notification.service.ts
│ └── shared.module.ts
│
├── common/ # Gemeinsame Utilities
│ ├── interfaces/
│ │ ├── api-response.interface.ts
│ │ ├── pagination.interface.ts
│ │ └── audit.interface.ts
│ ├── enums/
│ │ ├── order-status.enum.ts
│ │ ├── user-role.enum.ts
│ │ └── payment-status.enum.ts
│ ├── constants/
│ │ ├── app.constants.ts
│ │ ├── cache.constants.ts
│ │ └── queue.constants.ts
│ ├── utils/
│ │ ├── date.utils.ts
│ │ ├── validation.utils.ts
│ │ └── pagination.utils.ts
│ ├── dto/
│ │ ├── pagination.dto.ts
│ │ └── base-response.dto.ts
│ └── entities/
│ └── base.entity.ts
│
├── config/ # Konfigurationsdateien
│ ├── database.config.ts
│ ├── jwt.config.ts
│ ├── redis.config.ts
│ ├── email.config.ts
│ ├── upload.config.ts
│ └── app.config.ts
│
├── database/ # Datenbank-spezifische Dateien
│ ├── migrations/
│ │ ├── 1640000000000-CreateUsers.ts
│ │ ├── 1640000001000-CreateProducts.ts
│ │ └── 1640000002000-CreateOrders.ts
│ ├── seeds/
│ │ ├── user.seed.ts
│ │ └── product.seed.ts
│ └── data-source.ts
│
├── jobs/ # Background Jobs
│ ├── processors/
│ │ ├── email.processor.ts
│ │ ├── image-processing.processor.ts
│ │ └── order-processing.processor.ts
│ └── jobs.module.ts
│
├── app.controller.ts
├── app.service.ts
├── app.module.ts
└── main.ts
Für sehr komplexe Anwendungen kann eine Domain-Driven Design-Struktur angemessen sein:
src/
├── domains/ # Domain-spezifische Module
│ ├── user-management/
│ │ ├── domain/
│ │ │ ├── entities/
│ │ │ ├── value-objects/
│ │ │ ├── repositories/
│ │ │ └── services/
│ │ ├── infrastructure/
│ │ │ ├── repositories/
│ │ │ └── services/
│ │ ├── application/
│ │ │ ├── commands/
│ │ │ ├── queries/
│ │ │ └── handlers/
│ │ └── presentation/
│ │ ├── controllers/
│ │ └── dto/
│ │
│ ├── order-management/
│ └── inventory-management/
│
├── shared-kernel/ # Domain-übergreifende Komponenten
│ ├── domain/
│ │ ├── base-entity.ts
│ │ └── domain-event.ts
│ ├── infrastructure/
│ │ ├── event-bus.ts
│ │ └── unit-of-work.ts
│ └── application/
│ └── base-handler.ts
│
└── infrastructure/ # Technische Infrastruktur
├── database/
├── messaging/
├── caching/
└── external-services/
src/
├── microservices/ # Service-spezifische Module
│ ├── user-service/
│ │ ├── controllers/
│ │ ├── services/
│ │ ├── dto/
│ │ └── user-service.module.ts
│ ├── order-service/
│ ├── payment-service/
│ └── notification-service/
│
├── gateways/ # API Gateways
│ ├── rest-gateway/
│ ├── graphql-gateway/
│ └── websocket-gateway/
│
├── shared/ # Service-übergreifende Komponenten
│ ├── events/
│ ├── clients/
│ └── protocols/
│
└── infrastructure/ # Infrastruktur-Services
├── message-broker/
├── service-discovery/
└── monitoring/
Konsistente Namensgebung erleichtert die Navigation und das Verständnis der Codebase:
Dateien und Verzeichnisse: - Verwenden Sie
kebab-case für Dateinamen: user-profile.service.ts - Nutzen
Sie deskriptive Suffixe: .controller.ts,
.service.ts, .module.ts - Gruppieren Sie
verwandte Dateien in Verzeichnissen: users/dto/,
users/entities/
Klassen und Interfaces: - PascalCase für Klassen:
UserProfileService, CreateUserDto -
Interface-Namen mit “I” präfixen oder deskriptiv:
IUserRepository, UserRepositoryInterface -
Abstrakte Klassen mit “Abstract” präfixen:
AbstractBaseService
Variablen und Methoden: - camelCase für Variablen
und Methoden: findActiveUsers(), userProfile -
Deskriptive Namen: getUserByEmail() statt
getUser()
Strukturierte Imports verbessern die Lesbarkeit:
// Externe Libraries (Alphabetisch)
import { Injectable, NotFoundException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
// Relative Imports (nach Verzeichnistiefe)
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
import { User } from './entities/user.entity';
// Absolute Imports mit Path Mapping
import { EmailService } from '@shared/services/email.service';
import { PaginationDto } from '@common/dto/pagination.dto';
import { UserRole } from '@common/enums/user-role.enum';src/
├── common/
│ ├── exceptions/
│ │ ├── business.exception.ts
│ │ ├── validation.exception.ts
│ │ └── not-found.exception.ts
│ └── filters/
│ ├── global-exception.filter.ts
│ ├── business-exception.filter.ts
│ └── validation-exception.filter.ts
Parallele Test-Struktur zur Anwendungsstruktur:
src/
├── modules/
│ └── users/
│ ├── users.service.ts
│ ├── users.service.spec.ts # Unit Tests
│ ├── users.controller.ts
│ └── users.controller.spec.ts # Unit Tests
│
test/
├── e2e/
│ ├── users.e2e-spec.ts # E2E Tests
│ └── auth.e2e-spec.ts
├── integration/
│ ├── database.integration.spec.ts # Integration Tests
│ └── cache.integration.spec.ts
└── fixtures/
├── user.fixture.ts # Test-Daten
└── product.fixture.ts
.env.development # Entwicklungsumgebung
.env.staging # Staging-Umgebung
.env.production # Produktionsumgebung
.env.test # Test-Umgebung
.env.example # Beispiel-Template
Beispiel .env.development:
# Application
NODE_ENV=development
PORT=3000
API_VERSION=v1
# Database
DB_HOST=localhost
DB_PORT=5432
DB_USERNAME=dev_user
DB_PASSWORD=dev_password
DB_NAME=myapp_dev
# JWT
JWT_SECRET=your-dev-secret-key
JWT_EXPIRES_IN=1h
JWT_REFRESH_SECRET=your-dev-refresh-secret
JWT_REFRESH_EXPIRES_IN=7d
# Redis
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=
# Email
SMTP_HOST=localhost
SMTP_PORT=1025
SMTP_USERNAME=
SMTP_PASSWORD=
# File Upload
UPLOAD_DESTINATION=./uploads
MAX_FILE_SIZE=5242880
# External APIs
PAYMENT_API_URL=https://sandbox.payment-provider.com
PAYMENT_API_KEY=sandbox_key
# Monitoring
LOG_LEVEL=debug
ENABLE_SWAGGER=true{
"scripts": {
"start": "nest start",
"start:dev": "nest start --watch",
"start:debug": "nest start --debug --watch",
"start:prod": "node dist/main",
"build": "nest build",
"build:webpack": "nest build --webpack",
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
"test": "jest",
"test:watch": "jest --watch",
"test:cov": "jest --coverage",
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "jest --config ./test/jest-e2e.json",
"db:migration:generate": "npm run typeorm -- migration:generate src/database/migrations/$npm_config_name",
"db:migration:run": "npm run typeorm -- migration:run",
"db:migration:revert": "npm run typeorm -- migration:revert",
"db:seed": "ts-node src/database/seeds/seed.ts",
"typeorm": "ts-node --require tsconfig-paths/register ./node_modules/typeorm/cli.js --config src/database/data-source.ts"
}
}{
"husky": {
"hooks": {
"pre-commit": "lint-staged",
"pre-push": "npm run test && npm run build"
}
},
"lint-staged": {
"*.{ts,tsx}": [
"eslint --fix",
"prettier --write"
]
}
}# Dockerfile.dev
FROM node:20-alpine
WORKDIR /app
# Copy package files
COPY package*.json ./
RUN npm ci
# Copy source code
COPY . .
# Expose port
EXPOSE 3000
EXPOSE 9229
# Start development server
CMD ["npm", "run", "start:debug"]# docker-compose.dev.yml
version: '3.8'
services:
app:
build:
context: .
dockerfile: Dockerfile.dev
ports:
- "3000:3000"
- "9229:9229"
volumes:
- .:/app
- /app/node_modules
environment:
- NODE_ENV=development
depends_on:
- postgres
- redis
postgres:
image: postgres:15-alpine
environment:
POSTGRES_DB: myapp_dev
POSTGRES_USER: dev_user
POSTGRES_PASSWORD: dev_password
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
postgres_data:Eine gut strukturierte NestJS-Anwendung ist die Grundlage für erfolgreiche Langzeitprojekte. Die hier präsentierten Strukturen und Best Practices helfen dabei, skalierbare und wartbare Anwendungen zu entwickeln, die auch bei wachsender Komplexität übersichtlich und verständlich bleiben.