Dependency Injection in NestJS: How It Works

@rnab
3 min readDec 22, 2024

--

Dependency Injection (DI) is a design pattern that allows a class to receive its dependencies from external sources rather than creating them itself. This can improve the modularity and maintainability of your applications by decoupling class dependencies from their implementations. NestJS, a progressive Node.js framework, makes extensive use of dependency injection out of the box. In this article, we’ll explore how DI works in NestJS and why it’s an essential part of building scalable and testable applications.

What Is Dependency Injection?

In traditional software design, classes often instantiate their dependencies directly. This approach can lead to tightly coupled and difficult-to-test code. Dependency Injection addresses this issue by allowing us to inject dependencies into a class from an external source — typically, a framework or a container.

How NestJS Handles Dependency Injection

NestJS utilizes its own dependency injection container, which makes it easy to manage the lifecycle of dependencies and their scopes. NestJS provides two decorators, @Injectable() and @Inject(), which are essential for working with DI.

  1. @Injectable(): Marks a class as a candidate for dependency injection.
  2. @Inject(): Allows the injection of a specific provider into a class.

Setting Up Dependency Injection in NestJS

Let’s walk through an example to see how dependency injection works in NestJS.

Step 1: Create a Service

First, create a simple service using the @Injectable() decorator. This service will be responsible for basic operations, in this case, providing a greeting.

// src/hello.service.ts
import { Injectable } from '@nestjs/common';

@Injectable()
export class HelloService {
getHello(): string {
return 'Hello, World!';
}
}

Step 2: Inject the Service into a Controller

Next, you need to inject the HelloService into a controller so you can use its getHello() method. Use the @Injectable() and @Inject() decorators to accomplish this.

// src/hello.controller.ts
import { Controller, Get } from '@nestjs/common';
import { HelloService } from './hello.service';

@Controller('hello')
export class HelloController {
constructor(private readonly helloService: HelloService) {}

@Get()
getHello(): string {
return this.helloService.getHello();
}
}

Step 3: Register the Service in a Module

Finally, register the HelloService in a module, so that NestJS knows where to look for this provider.

// src/hello.module.ts
import { Module } from '@nestjs/common';
import { HelloController } from './hello.controller';
import { HelloService } from './hello.service';

@Module({
imports: [],
controllers: [HelloController],
providers: [HelloService],
})
export class HelloModule {}

Step 4: Add the Module to the Root Module

Ensure that your custom module is included in the root module to make it part of the application.

// src/app.module.ts
import { Module } from '@nestjs/common';
import { HelloModule } from './hello/hello.module';

@Module({
imports: [HelloModule],
controllers: [],
providers: [],
})
export class AppModule {}

Running the Application

With everything set up, you can run the application and test if the dependency injection works as expected.

npm run start

Visit http://localhost:3000/hello in a browser or use a tool like curl to see the output:

curl http://localhost:3000/hello

You should see:

Hello, World!

Conclusion

Dependency Injection is a powerful feature that can greatly enhance the maintainability and testability of your code. NestJS makes it straightforward to use DI with its intuitive decorators and module-based architecture. By following the steps in this article, you should have a good understanding of how DI works in NestJS and how to apply it to your projects.

Happy coding!

--

--

@rnab
@rnab

Written by @rnab

Typescript, Devops, Kubernetes, AWS, AI/ML, Algo Trading

No responses yet