6 NestJS Projektstruktur

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.

6.1 Grundlegende Verzeichnisstruktur

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.

6.1.1 Standard-Projektlayout

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

6.1.2 Verzeichnis-Zweck und Bedeutung

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.

6.1.3 Automatisch generierte Dateien

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 {}

6.2 Hauptbestandteile eines NestJS-Projekts

6.2.1 Source-Verzeichnis (src/)

Das src/-Verzeichnis bildet den Kern jeder NestJS-Anwendung und sollte methodisch organisiert werden, um Wartbarkeit und Verständlichkeit zu gewährleisten.

6.2.1.1 Kern-Dateien des Source-Verzeichnisses

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 {}

6.2.1.2 Modulare Struktur im Source-Verzeichnis

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

6.2.2 Konfigurationsdateien

NestJS-Projekte enthalten verschiedene Konfigurationsdateien, die das Verhalten der Anwendung und des Entwicklungsprozesses steuern.

6.2.2.1 TypeScript-Konfiguration

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"
  ]
}

6.2.2.2 NestJS CLI-Konfiguration

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"
    }
  }
}

6.2.2.3 Package.json Scripts

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"
  }
}

6.3 Aktuelle Funktionen in NestJS

6.3.1 TypeScript als Standard

NestJS wurde von Grund auf für TypeScript entwickelt und nutzt dessen fortgeschrittene Features für robuste Anwendungsarchitektur.

6.3.1.1 Dekorator-basierte Entwicklung

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);
  }
}

6.3.1.2 Erweiterte TypeScript-Features

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;
    }
  }
}

6.3.2 Verbesserte Konfigurationsdatei

Moderne NestJS-Anwendungen nutzen das @nestjs/config-Modul für typisierte Konfiguration:

6.3.2.1 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',
}));

6.3.2.2 Konfiguration-Injection

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}`;
  }
}

6.3.3 Dekoratoren und Metadaten

NestJS erweitert das TypeScript-Dekorator-System für umfangreiche Metadaten-Verarbeitung.

6.3.3.1 Custom Decorators

// 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();
  }
}

6.3.3.2 Metadaten-Reflection

// 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));
  }
}

6.4 Empfohlene Projektstruktur für größere Anwendungen

Bei wachsender Komplexität sollte die Projektstruktur entsprechend skaliert und organisiert werden.

6.4.1 Feature-basierte Modularisierung

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

6.4.2 Domain-Driven Design Struktur

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/

6.4.3 Microservices-orientierte Struktur

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/

6.5 Best Practices

6.5.1 Naming Conventions

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()

6.5.2 Import-Organization

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';

6.5.3 Error Handling Structure

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

6.5.4 Testing Structure

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

6.6 Entwicklungsumgebung einrichten

6.6.1 Environment-spezifische Konfiguration

.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

6.6.2 Development Scripts

{
  "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"
  }
}

6.6.3 Git Hooks Setup

{
  "husky": {
    "hooks": {
      "pre-commit": "lint-staged",
      "pre-push": "npm run test && npm run build"
    }
  },
  "lint-staged": {
    "*.{ts,tsx}": [
      "eslint --fix",
      "prettier --write"
    ]
  }
}

6.6.4 Docker Development Setup

# 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.