<link rel="stylesheet" href="styles-3GBANQL6.css">
← Zurück zum Blog

NestJS

Dependency Injection in NestJS: Die Kraft von Decorators verstehen

Dependency Injection in NestJS: Die Kraft von Decorators verstehen

NestJS ist ein progressives Node.js-Framework, das entwickelt wurde, um effiziente, zuverlässige und skalierbare serverseitige Anwendungen zu erstellen. Eines seiner Kernprinzipien ist die Dependency Injection (DI), die einen strukturierten und modularen Ansatz für die Verwaltung von Abhängigkeiten in Ihrer Anwendung bietet. In diesem Blog tauchen wir tief in das DI-System von NestJS ein, erklären, wie Decorators wie @Injectable() das alles möglich machen und welche Rolle Metadaten bei der Verbesserung von DI spielen.

Was ist Dependency Injection?

Dependency Injection ist ein Entwurfsmuster, das es ermöglicht, Abhängigkeiten in eine Klasse einzuspeisen, anstatt sie fest in der Klasse zu kodieren. Dies fördert eine bessere Testbarkeit, Wartbarkeit und Wiederverwendbarkeit des Codes. In NestJS steht DI im Zentrum der Architektur und wird nahtlos durch Decorators und Metadaten integriert.

Wichtige Konzepte

  • Provider: Dies sind Klassen oder Werte, die in andere Klassen injiziert werden können.
  • Injector: NestJS verwendet Injector, um die richtigen Abhängigkeiten aufzulösen und bereitzustellen.
  • Decorator: Spezielle Marker, die verwendet werden, um Klassen, Methoden oder Eigenschaften Metadaten hinzuzufügen und NestJS anzuweisen, wie sie behandelt werden sollen.
  • Metadaten: Im Hintergrund liefern Metadaten dem Framework die notwendigen Informationen über die Abhängigkeiten.

Der @Injectable() Decorator

Im Zentrum des DI-Systems von NestJS steht der @Injectable()-Decorator. Dieser Decorator markiert eine Klasse als Provider, was bedeutet, dass die Klasse in eine andere Klasse über den Konstruktor injiziert werden kann.

Beispiel: Ein einfacher Service

    
import { Injectable } from '@nestjs/common';

@Injectable()
export class UserService {
  getUsers() {
    return ['John Doe', 'Jane Doe', 'Mary Jane'];
  }
}
    
  

In diesem Beispiel wird der UserService mit dem @Injectable()-Decorator versehen, was NestJS anweist, dass diese Klasse ein Provider ist und in andere Komponenten wie Controller oder andere Services injiziert werden kann.

Abhängigkeiten in einem Controller injizieren

Um die Kraft von DI zu demonstrieren, injizieren wir diesen UserService in einen Controller:

    
import { Controller, Get } from '@nestjs/common';
import { UserService } from './user.service';

@Controller('users')
export class UserController {
  constructor(private readonly userService: UserService) {}

  @Get()
  findAll() {
    return this.userService.getUsers();
  }
}
    
  

Hier hängt der UserController von UserService ab, um eine Liste von Benutzern zu erhalten. Beachten Sie, dass wir keine Instanz von UserService manuell erstellt haben – sie wurde über den Konstruktor in den Controller injiziert. Dies entkoppelt den Controller von der Erstellung des Services und macht den Code modularer und besser testbar.

Wie funktioniert Dependency Injection in NestJS?

Im Hintergrund verwendet NestJS einen leistungsstarken Inversion of Control (IoC)-Container, der den Lebenszyklus aller Provider verwaltet. Wenn Sie eine Klasse mit @Injectable() dekorieren, registriert NestJS sie im IoC-Container. Wenn eine abhängige Klasse (wie ein Controller) diesen Provider benötigt, stellt der IoC-Container ihn bereit und löst automatisch alle Abhängigkeiten.

Benutzerdefinierte Provider mit dem @Inject()-Decorator

In manchen Fällen müssen Sie einen benutzerdefinierten Wert oder ein benutzerdefiniertes Objekt injizieren. NestJS ermöglicht dies mit dem @Inject()-Decorator:

    
import { Injectable, Inject } from '@nestjs/common';

@Injectable()
export class ConfigService {
  constructor(@Inject('DATABASE_URL') private databaseUrl: string) {}

  getDatabaseUrl() {
    return this.databaseUrl;
  }
}
    
  

Hier injizieren wir einen String (DATABASE_URL) in den ConfigService. Dieser String könnte im Provider-Array eines Moduls definiert sein.

    
@Module({
  providers: [
    {
      provide: 'DATABASE_URL',
      useValue: 'mongodb://localhost:27017/mydb',
    },
    ConfigService,
  ],
})
export class AppModule {}
    
  

Provider-Scopes in NestJS

Standardmäßig sind Provider in NestJS Singletons, das heißt, eine einzelne Instanz des Providers wird in der gesamten Anwendung geteilt. Sie können jedoch den Scope eines Providers mit der scope-Option ändern:

  • Singleton: Eine Instanz für die gesamte App (Standard).
  • Request-scoped: Eine neue Instanz für jede Anfrage.
  • Transient: Eine neue Instanz für jede Injektion.

So können Sie einen Request-scope Provider definieren:

    
@Injectable({ scope: Scope.REQUEST })
export class ScopedService {
  // Diese Instanz wird pro Anfrage erstellt
}
    
  

Decorators und Metadaten

NestJS verwendet intensiv Decorators, eine TypeScript-Funktion, die es ermöglicht, Klassen, Methoden und Eigenschaften Verhalten hinzuzufügen. Decorators liefern Metadaten, die NestJS verwendet, um zu konfigurieren, wie das DI-System funktioniert.

Zum Beispiel speichert NestJS Metadaten über diese Klassen und Methoden, wenn Sie die @Controller(), @Injectable() oder @Get()-Decorators verwenden. Diese Metadaten werden später vom IoC-Container verwendet, um Abhängigkeiten zu injizieren, Anfragen zu bearbeiten und andere Aufgaben auszuführen.

Sie können Metadaten auch manuell mit der TypeScript-Reflect-API abrufen oder definieren:

    
import 'reflect-metadata';

class Example {
  @Reflect.metadata('role', 'admin')
  getRole() {}
}

const role = Reflect.getMetadata('role', Example.prototype, 'getRole');
console.log(role); // Ausgabe: 'admin'
    
  

Obwohl Sie die Reflect-API in den meisten Fällen nicht manuell verwenden müssen, kann das Verständnis, dass Decorators auf Metadaten basieren, Ihnen helfen, die Flexibilität des DI-Systems von NestJS zu schätzen.

Ein praxisnahes Beispiel: Ein Service mit Abhängigkeiten injizieren

Lassen Sie uns unser früheres Beispiel mit einem DatabaseService erweitern, von dem der UserService abhängt.

    
import { Injectable } from '@nestjs/common';

@Injectable()
export class DatabaseService {
  query(sql: string) {
    return `Ausführen der Abfrage: ${sql}`;
  }
}

@Injectable()
export class UserService {
  constructor(private readonly dbService: DatabaseService) {}

  getUsers() {
    return this.dbService.query('SELECT * FROM users');
  }
}
    
  

Hier hängt der UserService von DatabaseService ab, der über den Konstruktor injiziert wird. Wenn UserService.getUsers() aufgerufen wird, delegiert er an DatabaseService.query(). Dank DI müssen wir uns erneut keine Gedanken über die Erstellung oder Verwaltung von DatabaseService-Instanzen machen – NestJS übernimmt das für uns.

Zusammenfassung

Das Dependency Injection-System von NestJS ist eine seiner leistungsstärksten Funktionen, die eine saubere, modulare und testbare Möglichkeit bietet, Abhängigkeiten in Ihrer Anwendung zu verwalten. Durch den Einsatz von Decorators wie @Injectable(), @Inject() und den IoC-Container macht es NestJS einfach, skalierbaren und wartbaren Code zu schreiben.

Wichtige Erkenntnisse:

  • @Injectable() wird verwendet, um eine Klasse als Provider zu markieren, wodurch sie injizierbar wird.
  • Provider werden durch Konstruktoren injiziert, was zu einer losen Kopplung führt.
  • NestJS verwendet Metadaten, die durch Decorators definiert werden, um das DI-System im Hintergrund zu verwalten.
  • Benutzerdefinierte Provider können mit @Inject() erstellt werden, und Provider-Scopes ermöglichen die Kontrolle über die Lebensdauer der Provider.

Mit diesem Verständnis von DI in NestJS sind Sie auf dem besten Weg, ausdrucksstarke, wartbare und skalierbare Anwendungen zu schreiben.

LASSEN SIE UNS ETWAS NEUES BAUEN

Aus Ihrer Idee kann die nächste gute Softwarelösung entstehen.

Erzählen Sie uns, was Sie entwickeln, verbessern oder neu denken möchten.

Projekt besprechen