Dependency injection in NestJS

Shalon N. NgigiShalon N. Ngigi
4 min read
  1. Getting started with nestjs

  2. An overview of nestjs - modules, controllers and providers

  3. Dependency injection in nestjs [you are here]

  4. Dependency injection in JavaScript [Bonus]

  5. Conclusion

What is Dependency Injection (DI)?

Dependency injection is a programming pattern where dependencies needed in a class or funciton are passed in as parameters instead of being instantiated inside the class/function.

DI allows a class to receive its dependencies from outside, rather than creating them itself. This way, the class does not need to know the details of how its dependencies are created, which promotes loose coupling.

💡
Check out this article to understand the fundamental principle of dependency injection (in JavaScript).

How Does DI Work in NestJS?

In NestJS, Dependency Injection is a core feature built into the framework. It allows us to easily manage and inject dependencies into classes such as controllers, services, and other providers.

  • Providers: any class annotated with the @Injectable() decorator can be treated as a provider and can be injected into other classes using DI.

  • Modules: providers are typically declared within modules, and the DI system uses the module's metadata to resolve and inject dependencies.

  • Injecting Dependencies: dependencies are injected into classes using the constructor. When you define a dependency in a class constructor, NestJS will automatically resolve and inject the required instance.

Here’s an example of 2 providers, a user service and an auth service.

AuthService is a provider (service) that depends on UsersService. Instead of the auth service creating an instance of UsersService itself, the AuthService has it injected via the constructor.

import { Injectable } from '@nestjs/common';

@Injectable()
export class UsersService {
  findAll() {
    return [{ id: 1, name: 'John Doe' }];
  }
}

@Injectable()
export class AuthService {
  constructor(private usersService: UsersService) {}

  validateUser(id: number) {
    const users = this.usersService.findAll();
    return users.find(user => user.id === id);
  }
}

Injecting a Provider

To inject a provider inside its own module, say the controller, here’s how you would do it:

  1. Declare the Provider in a Module: First, declare the provider in its module. Providers are declared in the providers array of the @Module() decorator.

     import { Module } from '@nestjs/common';
     import { UserService, AuthService } from './user.service';
    
     @Module({
       providers: [UserService, AuthService],
       controllers: [UserController],
     })
     export class UsersModule {}
    
  2. Inject the provider into any controller or provider by specifying it in the constructor

     import { Body, Controller, Get } from '@nestjs/common';
     import { UserService } from './user.service';
    
     @Controller('users')
     export class UserController {
       constructor(private userService: UserService) {}
    
       @Get()
       getAllUsers() {
         return this.userService.findAll();
       }
     }
    

In most cases you will want to access a provider in different modules. To inject a provider into another module, controller, or provider, follow these steps:

  1. Declare the Provider in a Module (Same step 1 as above): First, declare the provider in its own module (where you want it to be available). Providers are declared in the providers array of the @Module() decorator. Then export it by adding it to the exports array.

     import { Module } from '@nestjs/common';
     import { UsersService } from './users.service';
     import { AuthService } from './auth.service';
    
     @Module({
       providers: [UsersService, AuthService],
       exports: [UsersService], // Exporting to make it available in other modules
     })
     export class UsersModule {}
    
  2. Import the Module: If you want to use the UsersService in another module, you need to import the module that provides it.

     import { Module } from '@nestjs/common';
     import { UsersModule } from './users/users.module';
     import { SomeOtherService } from './some-other.service';
    
     @Module({
       imports: [UsersModule], // Importing UsersModule to access UsersService
       providers: [SomeOtherService],
     })
     export class SomeOtherModule {}
    
  3. Inject the Provider into a Controller or Another Provider: Now that the provider (UsersService) is available, you can inject it into any controller or provider by specifying it in the constructor.

    If you want to use UserService in SomeOtherService, do it like this

     import { Injectable } from '@nestjs/common';
     import { UsersService } from './users.service';
    
     @Injectable()
     export class SomeOtherService {
       constructor(private readonly usersService: UsersService) {}
    
       findSomeOtherService() {
         return this.usersService.findAll();
       }
     }
    

In summary to create a provider:

  1. Create a Provider: Define a class with the @Injectable() decorator.

  2. Declare the Provider in a Module: Add the provider to the providers array of a module.

  3. Export the Provider (if needed): If the provider needs to be used in another module, export it by adding it to the exports array.

  4. Import the Module (if needed): In the consuming module, import the module that exports the provider.

  5. Inject the Provider: Inject the provider into any class (controller, service, etc.) by specifying it in the constructor.

Providers can be used to create services, factories, repositories, helper utilities; you can even create your own custom providers and inject them wherever as needed.

In conclusion, next is pretty cool

0
Subscribe to my newsletter

Read articles from Shalon N. Ngigi directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Shalon N. Ngigi
Shalon N. Ngigi