Was macht NestJS-Entwicklungsmuster?
Produktionsreife NestJS-Muster für modulare TypeScript-Backends.
Wann aktivieren
- Erstellen von NestJS-APIs oder -Services
- Strukturieren von Modulen, Controllern und Providern
- Hinzufügen von DTO-Validierung, Guards, Interceptors oder Exception-Filtern
- Konfigurieren umgebungsabhängiger Einstellungen und Datenbankintegrationen
- Testen von NestJS-Einheiten oder HTTP-Endpunkten
Projektstruktur
src/
├── app.module.ts
├── main.ts
├── common/
│ ├── filters/
│ ├── guards/
│ ├── interceptors/
│ └── pipes/
├── config/
│ ├── configuration.ts
│ └── validation.ts
├── modules/
│ ├── auth/
│ │ ├── auth.controller.ts
│ │ ├── auth.module.ts
│ │ ├── auth.service.ts
│ │ ├── dto/
│ │ ├── guards/
│ │ └── strategies/
│ └── users/
│ ├── dto/
│ ├── entities/
│ ├── users.controller.ts
│ ├── users.module.ts
│ └── users.service.ts
└── prisma/ oder database/
- Halten Sie den Domain-Code innerhalb der Feature-Module.
- Platzieren Sie querschnittliche Filter, Dekorateure, Guards und Interceptors in
common/. - Behalten Sie DTOs in der Nähe des Moduls, dem sie gehören.
Bootstrap und globale Validierung
async function bootstrap() {
const app = await NestFactory.create(AppModule, { bufferLogs: true });
app.useGlobalPipes(
new ValidationPipe({
whitelist: true,
forbidNonWhitelisted: true,
transform: true,
transformOptions: { enableImplicitConversion: true },
}),
);
app.useGlobalInterceptors(new ClassSerializerInterceptor(app.get(Reflector)));
app.useGlobalFilters(new HttpExceptionFilter());
await app.listen(process.env.PORT ?? 3000);
}
bootstrap();
- Aktivieren Sie immer
whitelistundforbidNonWhitelistedfür öffentliche APIs. - Bevorzugen Sie eine globale Validierungspipe anstelle wiederholter Validierungskonfiguration pro Route.
Module, Controller und Provider
@Module({
controllers: [UsersController],
providers: [UsersService],
exports: [UsersService],
})
export class UsersModule {}
@Controller('users')
export class UsersController {
constructor(private readonly usersService: UsersService) {}
@Get(':id')
getById(@Param('id', ParseUUIDPipe) id: string) {
return this.usersService.getById(id);
}
@Post()
create(@Body() dto: CreateUserDto) {
return this.usersService.create(dto);
}
}
@Injectable()
export class UsersService {
constructor(private readonly usersRepo: UsersRepository) {}
async create(dto: CreateUserDto) {
return this.usersRepo.create(dto);
}
}
- Controller sollten schlank bleiben: HTTP-Eingaben parsen, einen Provider aufrufen und Antwort-DTOs zurückgeben.
- Platzieren Sie Geschäftslogik in injizierbaren Services, nicht in Controllern.
- Exportieren Sie nur die Provider, die andere Module tatsächlich benötigen.
DTOs und Validierung
export class CreateUserDto {
@IsEmail()
email!: string;
@IsString()
@Length(2, 80)
name!: string;
@IsOptional()
@IsEnum(UserRole)
role?: UserRole;
}
- Validieren Sie jedes Request-DTO mit
class-validator. - Verwenden Sie dedizierte Antwort-DTOs oder Serializer, anstatt ORM-Entitäten direkt zurückzugeben.
- Vermeiden Sie das Durchsickern interner Felder wie Passwort-Hashes, Tokens oder Audit-Spalten.
Authentifizierung, Guards und Request-Kontext
@UseGuards(JwtAuthGuard, RolesGuard)
@Roles('admin')
@Get('admin/report')
getAdminReport(@Req() req: AuthenticatedRequest) {
return this.reportService.getForUser(req.user.id);
}
- Halten Sie Auth-Strategien und Guards modullokal, es sei denn, sie sind wirklich gemeinsam genutzt.
- Kodieren Sie grobe Zugriffsregeln in Guards und führen Sie ressourcenspezifische Autorisierung in Services durch.
- Bevorzugen Sie explizite Request-Typen für authentifizierte Request-Objekte.
Exception-Filter und Fehlerstruktur
@Catch()
export class HttpExceptionFilter implements ExceptionFilter {
catch(exception: unknown, host: ArgumentsHost) {
const response = host.switchToHttp().getResponse<Response>();
const request = host.switchToHttp().getRequest<Request>();
if (exception instanceof HttpException) {
return response.status(exception.getStatus()).json({
path: request.url,
error: exception.getResponse(),
});
}
return response.status(500).json({
path: request.url,
error: 'Interner Serverfehler',
});
}
}
- Verwenden Sie eine einheitliche Fehlerhülle über die gesamte API hinweg.
- Werfen Sie Framework-Ausnahmen für erwartete Client-Fehler; protokollieren und umschließen Sie unerwartete Fehler zentral.
Konfiguration und Umgebungsvalidierung
ConfigModule.forRoot({
isGlobal: true,
load: [configuration],
validate: validateEnv,
});
- Validieren Sie Umgebungsvariablen beim Start, nicht erst bei der ersten Anfrage.
- Greifen Sie auf die Konfiguration nur über typisierte Hilfsfunktionen oder Konfigurationsservices zu.
- Trennen Sie Entwicklungs-/Staging-/Produktions-Aspekte in Konfigurationsfabriken, anstatt sie im Feature-Code zu verzweigen.
Persistenz und Transaktionen
- Halten Sie Repository-/ORM-Code hinter Providern, die die Domänensprache sprechen.
- Isolieren Sie bei Prisma oder TypeORM transaktionale Arbeitsabläufe in Services, die die Arbeitseinheit besitzen.
- Lassen Sie Controller keine mehrschrittigen Schreibvorgänge direkt koordinieren.
Testen
describe('UsersController', () => {
let app: INestApplication;
beforeAll(async () => {
const moduleRef = await Test.createTestingModule({
imports: [UsersModule],
}).compile();
app = moduleRef.createNestApplication();
app.useGlobalPipes(new ValidationPipe({ whitelist: true, transform: true }));
await app.init();
});
});
- Testen Sie Provider isoliert mit gemockten Abhängigkeiten.
- Fügen Sie Request-Level-Tests für Guards, Validierungspipes und Exception-Filter hinzu.
- Verwenden Sie in Tests dieselben globalen Pipes/Filter wie in der Produktion.
Produktionsstandards
- Aktivieren Sie strukturierte Protokollierung und Request-Korrelations-IDs.
- Beenden Sie den Prozess bei ungültiger Umgebung/Konfiguration, anstatt nur teilweise zu starten.
- Bevorzugen Sie asynchrone Provider-Initialisierung für DB/Cache-Clients mit expliziten Health-Checks.
- Halten Sie Hintergrundjobs und Event-Consumer in eigenen Modulen, nicht innerhalb von HTTP-Controllern.
- Machen Sie Ratenbegrenzung, Authentifizierung und Audit-Protokollierung für öffentliche Endpunkte explizit.