Skip to content

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:

ts
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.

  1. You make a class @Injectable()
  2. You register it in a module
  3. Nest creates one instance (singleton by default)
  4. Other classes can just ask for it in their constructors

🧙 The DI Lifecycle

text
Module loads ->
  Finds all @Injectable() classes ->
    Registers them as Providers ->
      Injects them wherever needed ->
        Magic ✨

🍕 Let's See a Pizza Shop Example

ts
@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:

ts
@Module({
  providers: [PizzaService, SauceService],
})

If it’s in another module, export and import like a pro chef:

ts
@Module({
  providers: [SauceService],
  exports: [SauceService],
})

Then import it into your PizzaModule:

ts
@Module({
  imports: [SauceModule],
  providers: [PizzaService],
})

🧪 Testing with DI (Because you break less stuff)

With DI, testing is as smooth as cold brew:

ts
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:

ts
@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. 🍕

Built by noobs, for noobs, with love 💻❤️