post: Added provider and dependency injection one
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
This commit is contained in:
133
content/posts/provider-and-de-in-nestjs.md
Normal file
133
content/posts/provider-and-de-in-nestjs.md
Normal 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
|
||||||
|
|
||||||
Reference in New Issue
Block a user