La pluie cognaît contre les vitres du poste. Sur le tableau, des fils rouges relient des photos, des notes, des noms. Chaque fil raconte une histoire. Chaque connexion cache un secret. L'Injection de Dépendances dans NestJS, c'est ce tableau. Un réseau souterrain où les services se parlent sans jamais se toucher. Un système de tuyauteries invisibles où coule la logique de votre application.
2.1. Le Contact : Ce que vous croyez savoir
Vous pensez comprendre. Un service A a besoin d'un service B. Vous l'importez. Vous l'instanciez. Simple.
// L'ancienne manière. La manière dangereuse.
class UsersController {
private usersService: UsersService;
constructor() {
this.usersService = new UsersService(); // Vous tenez l'arme.
}
}
Problème n°1 : Vous êtes maintenant marié à UsersService. Pour le tester, vous devez tricher avec ses dépendances internes. Pour le changer, vous devez retoucher chaque contrôleur qui l'appelle. C'est comme si chaque inspecteur devait fabriquer son propre arme de service.
Problème n°2 : Et si UsersService a besoin d'un DatabaseService lui-même dépendant d'un ConfigService ? Vous voilà à empiler des new comme des cercueils dans un fourgon. L'application démarre et c'est l'effondrement.
2.2. Le Témoin Silencieux : Le Conteneur IoC
NestJS ne fonctionne pas comme ça. Il a un Conteneur IoC (Inversion of Control). Traduction : c'est le grand ordonnateur. Le cerveau qui sait qui dépend de qui.
Quand vous écrivez :
@Controller('users')
export class UsersController {
constructor(private readonly usersService: UsersService) {}
// Pas de 'new'. Juste une déclaration.
}
Voici ce qui se passe dans l'ombre :
- La Demande : NestJS voit le paramètre
usersServicede typeUsersService. - L'Enquête : Il va chercher dans ses registres. Qui est
UsersService? Où vit-il ? Est-il déjà créé ? - La Fabrication : Si
UsersServicen'existe pas, NestJS le crée. Mais attention : il regarde d'abord son constructeur.UsersServicea besoin d'unDatabaseService? NestJS crée d'abord celui-là. C'est une enquête récursive. - La Livraison : Il injecte l'instance terminée dans votre contrôleur. Propre. Invisible.
C'est ça, l'Injection de Dépendances : Demander, ne pas prendre.
2.3. Les Règles du Jeu : Les Fournisseurs (Providers)
Un Provider dans NestJS, c'est n'importe quoi que vous pouvez injecter. Un service, un repository, une factory, une valeur. Tout est défini dans le tableau providers d'un module.
@Module({
providers: [
UsersService, // Le classique
DatabaseService,
{ provide: 'API_KEY', useValue: process.env.API_KEY }, // Une valeur simple
{ provide: 'CONNECTION', useFactory: async () => { // Une factory
return await createConnection();
}},
{ provide: AbstractService, useClass: ConcreteService } // Une abstraction
]
})
export class AppModule {}
Le Token : Le provide: est la clé. Habituellement, c'est le nom de la classe (UsersService). Mais ça peut être une chaîne ('API_KEY') ou un symbole. C'est le nom sous lequel on demandera la dépendance.
Le use* : C'est la recette de fabrication.
useClass: Instancie cette classe (le cas le plus courant).useValue: Utilise cette valeur telle quelle.useFactory: Appelle cette fonction pour créer la dépendance.useExisting: Alias vers un autre provider déjà défini.
2.4. Les Portes du Réseau : Les Scopes d'Injection
Toutes les dépendances ne vivent pas aussi longtemps. NestJS gère leur durée de vie :
- SINGLETON (par défaut) : Une seule instance pour toute l'app. Tous les contrôleurs partagent le même service. Comme le registre central du poste de police. Économique, mais état partagé.
- REQUEST : Une nouvelle instance est créée pour chaque requête HTTP. L'instance est détruite à la fin de la requête. Comme un dossier d'enquête ouvert et fermé pour chaque affaire. Isolé, mais coûteux.
- TRANSIENT : Une nouvelle instance à chaque injection. Même deux services dans la même requête auront des instances différentes.
Pour changer la portée :
@Injectable({ scope: Scope.REQUEST })
export class RequestScopedService {
// Né et meurt avec chaque requête
}
2.5. Les Passages Secrets : L'Injection Personnalisée
Parfois, la dépendance n'est pas une classe. Parfois vous voulez injecter un objet de configuration, une librairie externe.
Injecter par clé :
constructor(
@Inject('API_KEY') private readonly apiKey: string,
@Inject('HTTP_CLIENT') private readonly httpClient: any
) {}
Le Pattern de Configuration (un classique) :
// config.service.ts
@Injectable()
export class ConfigService {
constructor(@Inject('CONFIG') private readonly config: any) {}
get databaseUrl(): string {
return this.config.database.url;
}
}
// app.module.ts
@Module({
providers: [
ConfigService,
{
provide: 'CONFIG',
useFactory: () => yaml.load(fs.readFileSync('config.yaml', 'utf8'))
}
]
})
2.6. La Pièce Maîtresse : L'Injection dans les Tests
C'est là que tout s'éclaire. Pour tester un contrôleur en isolation, vous ne voulez pas d'un vrai UsersService. Vous voulez un faux. Un témoin coopératif.
// Dans votre test
const mockUsersService = {
findUser: jest.fn().mockReturnValue({ id: '1', name: 'Test' })
};
beforeEach(async () => {
const module = await Test.createTestingModule({
controllers: [UsersController],
})
.overrideProvider(UsersService) // On intercepte la demande
.useValue(mockUsersService) // On répond avec notre faux
.compile();
controller = module.get(UsersController);
});
Le réseau souterrain est temporairement détourné. Votre contrôleur reçoit le mock sans le savoir. Vous testez son comportement, pas celui du vrai service.
2.7. Le Cadavre dans le Placard : Les Erreurs Courantes
-
Dépendance Circulaire :
Adépend deBqui dépend deA. NestJS le détectera avec une erreur claire. Solution : refactor, ou utiliserforwardRef(() => Service)en dernier recours. -
Provider Non Enregistré : Vous demandez
UsersServicedans un module où il n'est pas dansproviders. Ou il n'est pasexportédepuis son module d'origine. L'erreur :Nest can't resolve dependencies... -
Scope Mismatch : Injecter un service
REQUESTdans un serviceSINGLETON. Impossible. Le singleton vit plus longtemps. NestJS vous arrêtera.
Épilogue du Chapitre 2
L'Injection de Dépendances n'est pas un détail technique. C'est l'architecture nerveuse de votre application. Chaque constructor(private service: Service) est un fil tendu dans l'ombre. Ces fils ne s'emmêlent pas parce que NestJS joue le chef d'orchestre.
Vous ne créez plus. Vous déclarez un besoin. Vous ne couplez plus. Vous découplez. Vous ne testez plus avec difficulté. Vous mocker avec élégance.
Dans les chapitres sombres qui viennent, nous verrons comment ces services, une fois proprement injectés, traitent les données, interrogent les bases, et authentifient les requêtes. Mais souvenez-vous : tout repose sur ce réseau fantôme.
La prochaine fois que vous verrez un décorateur @Injectable(), souvenez-vous qu'il ne marque pas une classe. Il marque un témoin potentiel. Prêt à être appelé à la barre, quand son heure viendra.