Support Online
Skip to main content

NestJS Dependency Injection (DI) Guide: Why and How to Use It?

Meta Description: Develop modular applications using the Dependency Injection (DI) principle, basic decorators and Inversion of Control (IoC) mechanism in your NestJS projects.

📘 What You Will Learn in This Guide

  • The concept of Dependency Injection and how it is implemented in NestJS.
  • The logic of the IoC (Inversion of Control) mechanism.
  • Roles of basic NestJS decorators such as @Injectable(), @Module(), @Controller().
  • How to fix dependency parsing errors.

1️⃣ Concept of Dependency Injection (DI)

Dependency Injection is the principle of providing the dependencies needed by a class from outside.
In this way, a class takes an instance of another class from outside instead of creating it itself.

Based on this pattern, NestJS turns tightly coupled structures into loosely coupled.


🔒 Example of Tight Commitment Without DI

This example shows the classic structure where the dependency is created directly.

// Kendi bağımlılığını kendi oluşturuyor.
class VeriTabanıServisi {
baglan() {
console.log(`Veritabanı bağlantısı başlatıldı.`);
}
}

class MakaleServisi {
constructor() {
this.vtServisi = new VeriTabanıServisi(); // Sıkı Bağlılık
}

makaleOlustur(baslik) {
return this.vtServisi.olustur(baslik);
}
}

In this example, ArticleService is directly dependent on DatabaseService. It is necessary to change this code when the database type changes.

🧩 Loose Cohesion with NestJS

This example shows NestJS' DI container automatically injecting classes.


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

@Injectable()
class VeriTabanıServisi {
kaydet(veri) {
console.log(`"${veri.baslik}" başlıklı makale kaydedildi.`);
}
}

@Injectable()
class MakaleServisi {
constructor(private vtServisi: VeriTabanıServisi) {} // Yapıcı Enjeksiyonu

makaleOlustur(baslik, icerik) {
this.vtServisi.kaydet({ baslik, icerik });
}
}

In this example, ArticleService no longer uses new. The dependency (vtService) is injected automatically via the constructor. NestJS manages these classes through the DI container.


2️⃣ Inversion of Control (IoC)

IoC (Inversion of Control) is the architectural principle that NestJS uses to manage dependencies. While in traditional flow the program controls the flow of objects, in IoC NestJS takes over this control.

In short:

Instead of your code calling dependencies, NestJS brings the dependencies to you.

NestJS's IoC Container manages the creation and lifecycle of all providers. This makes your application more modular, testable and scalable.

💡 Example flow: AppModule → OyunModulu → OyunServisi → OyunController In this chain, NestJS first creates the providers and then injects them into the controller.


3️⃣ Basic Decorators and Their Roles in NestJS

NestJS uses TypeScript Decorators to define the structure of the application. Decorators determine how classes will be handled by the DI container.

🧱 A. @Injectable()

Specifies that a class can be managed by the NestJS DI system.


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

@Injectable()
export class OyuncuServisi {
getOyuncular() {
return [
{ id: 1, ad: 'Lionel Messi' },
{ id: 2, ad: 'Cristiano Ronaldo' },
];
}
}

This decorator turns the class into a provider. NestJS creates an instance of this class in its own container and injects it into other components.

🎮 B. @Controller()

It defines a class as the controller that processes HTTP requests.


import { Controller, Get } from '@nestjs/common';
import { OyuncuServisi } from './oyuncu.servisi';

@Controller('oyuncular')
export class OyuncularDenetleyici {
constructor(private readonly oyuncuServisi: OyuncuServisi) {}

@Get()
getOyuncular() {
return this.oyuncuServisi.getOyuncular();
}
}

NestJS automatically injects the PlayerService when it creates the controller. So when the GET /players request is called, the service returns the data.

🧩 C. @Module()

It organizes the building blocks of the application: controllers, providers and imported modules.


import { Module } from '@nestjs/common';
import { OyuncularDenetleyici } from './oyuncular.den';
import { OyuncuServisi } from './oyuncu.servisi';

@Module({
controllers: [OyuncularDenetleyici],
providers: [OyuncuServisi],
})
export class OyuncularModulu {}

When the NestJS application starts, it looks at the @Module() metadata. It creates instances of classes in the providers array and injects them into components in the controllers array.

⚠️ Error Troubleshooting: “Unable to Resolve Dependency” Error Common error among beginners:


@Module({
controllers: [OyuncularDenetleyici],
providers: [], // OyuncuServisi eksik!
})
export class OyuncularModulu {}

In this case, NestJS gives the following error:

❌ Can't resolve dependency of OyuncularDenetleyici.

✅ Solution:

Make sure that the provider used (ActorServisi) is defined in the providers array of the relevant module.


❓ Frequently Asked Questions (FAQ)

  1. What are the benefits of using DI?

Dependency Injection keeps code loosely coupled, making it easier to test. You can quickly run unit tests by mocking dependencies.

  1. When to use @Injectable() in NestJS?

If a class will be used by another class (for example, services), it must be marked with @Injectable().

  1. What do decorators do?

Decorators provide metadata that determines how classes will be managed by NestJS. In other words, NestJS knows which class is the provider and which is the controller with this information.

  1. What does the imports array do?

imports provides access to providers exported by another module. So when a module is imported, you can only access the exported components.


🏁 Result

In this guide, you learned about NestJS' Dependency Injection (DI) system and how the IoC container works. You've also discovered how to create modular, flexible, and testable applications using basic decorators.

🚀 You can try these techniques in your GenixNode projects right now!