post: Added provider and dependency injection one
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful

This commit is contained in:
SauravDhakal
2026-09-28 21:16:49 +05:45
parent ab36028ccf
commit efb76f222a

View File

@@ -0,0 +1,133 @@
---
title: "Understanding Providers and Dependency Injection in NestJS"
date: 2026-09-28T18:50:00+05:45
draft: false
tags: ["nestjs", "backend"]
description: "Concept of dependency injection in NestJS"
---
When I first started working with NestJS, registering providers (and controllers) inside a module felt like nothing more than a chore.
It was one of those things I did because *“that’s just how Nest is designed.”*
Even after I learned that modules exist to build an application graph, which it uses internally to resolve relationships and dependencies between modules and providers, the idea still felt vague and abstract. I understood **what** was happening, but not really **why** it worked the way it did.
Recently, I decided to take a deeper dive into how providers are registered and how Nest actually injects them. It turned out to be far more interesting than I expected.
Here’s what I learned.
## First, let’s understand what Dependency Injection is.
Wikipedia defines Dependency Injection(DI) as:
> **Dependency Injection** is a programming technique in which an object or function receives other objects or functions that it requires, as opposed to creating them internally.
Let’s look at a simple example.
Imagine a restaurant application. When a user’s order is ready for pickup, a notification needs to be sent.
```tsx
class OrderService {
// code operating over orders
const notificationService = new NotificationService()
notificationService.notify(userId, message)
// ...
}
```
Here, `OrderService` handles order-related logic, and `NotificationService` handles notifications.
However, because `NotificationService` is instantiated directly inside `OrderService` using `new`, this **is not** dependency injection.
Now compare that with this:
```tsx
class OrderService {
constructor(
private readonly notificationService: NotificationService,
) {}
// code operating over orders
notificationService.notify(userId, message)
// ...
}
```
This is dependency injection.
`OrderService` no longer cares about **how** `NotificationService` is created. It simply declares that it needs one, and something else provides it. That instance can also be cached and reused across every class that needs it, if configuration is same.
## Why Dependency Injection?
Dependency Injection helps by:
- Reducing coupling between classes
- Making testing easier (you can inject mock dependencies)
- Encouraging modular, maintainable code
## How Dependency Injection Works in NestJS
In NestJS, the basic flow looks something like this:
1. Define a provider using `@Injectable()`
2. Register that provider with Nest
3. Ask Nest to inject it using constructor-based injection
Registration usually happens in a module:
```tsx
@Module({
providers: [DemoService],
controllers: [DemoController],
})
export class DemoModule {}
```
At this point, Nest becomes responsible for creating and managing instances of `DemoService`.
## Nest’s IoC Container (The Missing Piece)
NestJS uses an **IoC (Inversion of Control) container** to manage dependencies.
You can think of this container as a **key-value** **registry**:
- **Keys** are called *tokens*
- **Values** describe how to create or retrieve a dependency
When we write:
```tsx
providers: [DemoService]
```
Nest internally expands it into a standard provider definition:
```tsx
providers: [
{
provide: DemoService,
useClass: DemoService,
},
]
```
In this case:
- The **token** is `DemoService` (the class itself)
- `useClass` tells Nest which class to instantiate when that token is requested.
So when Nest encounters:
```tsx
constructor(private readonly demoService: DemoService) {}
```
1. Nest looks up the `DemoService` token in the IoC container
2. It finds the matching provider record
3. It resolves the provider (`useClass` in this case)
4. The instance is created (or reused, since providers are singletons by default)
5. The resolved instance is injected into the class