Dependency Injection — Nest’s Magic Sauce
Welcome to the wizardry of NestJS 🧙♂️, where objects just appear where they’re needed — like snacks in a dev meeting.
This chapter is about Dependency Injection (DI), the behind-the-scenes spell that makes your app modular, clean, and test-friendly.
💡 What is Dependency Injection?
In plain devglish:
Instead of a class creating the things it needs, it asks for them, and Nest hands them over like a friendly but opinionated butler.
For example:
constructor(private readonly catsService: CatsService) {}
The CatsController
didn’t create CatsService
— it just asked nicely.
🧃 Why Use DI? (Besides looking cool)
- Separation of concerns: Logic lives where it belongs 🧼
- Easier testing: Inject mocks like a boss 💉
- Cleaner code: No more
new SomeService()
scattered everywhere 🤢 - Loose coupling: Your code becomes a smooth jazz band, not a tangled mess 🎷
🏗️ How It Works in Nest
Nest uses providers under the hood to manage dependencies.
- You make a class
@Injectable()
- You register it in a module
- Nest creates one instance (singleton by default)
- Other classes can just ask for it in their constructors
🧙 The DI Lifecycle
Module loads ->
Finds all @Injectable() classes ->
Registers them as Providers ->
Injects them wherever needed ->
Magic ✨
🍕 Let's See a Pizza Shop Example
@Injectable()
export class SauceService {
getSauce() {
return "Tomato magic 🍅✨";
}
}
@Injectable()
export class PizzaService {
constructor(private readonly sauceService: SauceService) {}
makePizza() {
return `Pizza with ${this.sauceService.getSauce()}`;
}
}
📦 Registering a Service
If it’s in the same module:
@Module({
providers: [PizzaService, SauceService],
})
If it’s in another module, export and import like a pro chef:
@Module({
providers: [SauceService],
exports: [SauceService],
})
Then import it into your PizzaModule:
@Module({
imports: [SauceModule],
providers: [PizzaService],
})
🧪 Testing with DI (Because you break less stuff)
With DI, testing is as smooth as cold brew:
const moduleRef = await Test.createTestingModule({
providers: [PizzaService, SauceService],
}).compile();
const pizzaService = moduleRef.get<PizzaService>(PizzaService);
No need to create the objects yourself — DI serves them up fresh! 🍽️
🔁 Scope: Singleton, Request, or Transient?
By default, providers are singleton. But you can change it:
@Injectable({ scope: Scope.REQUEST })
Options:
- Singleton: One for all (default)
- Request: New one per request
- Transient: New one every time you inject it
🏁 Final Thoughts
Dependency Injection is the kitchen staff of NestJS:
- Always working
- Never complaining
- Making your life easier one service at a time 🍳
Without DI, your code would be tighter than production deploys on Friday night. With it, your code breathes, laughs, and even makes pizza. 🍕