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