Sling Academy
Home/Node.js/How to Use Dependency Injection in NestJS

How to Use Dependency Injection in NestJS

Last updated: January 01, 2024

Introduction

Dependency Injection (DI) is a powerful design pattern that NestJS harnesses to create loosely coupled, maintainable, and scalable applications. This tutorial will guide you through the process of using DI in NestJS, starting with the basics and progressing to more advanced concepts.

Basic DI in NestJS

In NestJS, providers such as services, repositories, or custom providers can be injected into controllers or other services. Let’s start with a simple example:

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

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

import { Controller, Get } from '@nestjs/common';
import { MyService } from './my.service';

@Controller('hello')
export class MyController {
  constructor(private myService: MyService) {}

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

This is dependency injection at its simplest in NestJS:

  1. Create a service marked with the @Injectable() decorator.
  2. Inject the service into the constructor of a controller or another service.
  3. Use the injected service within the class methods.

Custom Providers

Sometimes you may need to provide a custom implementation or value. Here’s how you can use custom providers:

import { Module, Injectable, Inject } from '@nestjs/common';

const CUSTOM_VALUE = 'CUSTOM_VALUE';

@Injectable()
export class SomeService {
  constructor(@Inject(CUSTOM_VALUE) private value: string) {}

  getValue(): string {
    return this.value;
  }
}

@Module({
  providers: [
    {
      provide: CUSTOM_VALUE,
      useValue: 'This is a custom value',
    },
    SomeService,
  ],
})
export class SomeModule {}

In the above code, we are using the useValue syntax to inject a constant value.

Using Class Providers

Class providers allow you to map abstract classes or interfaces to concrete implementations:

import { Module, Injectable, Inject } from '@nestjs/common';

interface IExampleService {
  doSomething(): void;
}

@Injectable()
export class ExampleService implements IExampleService {
  public doSomething(): void {
    // Implementation here
  }
}

@Module({
  providers: [{ provide: 'IExampleService', useClass: ExampleService }],
})
export class ExampleModule {}

Asynchronous Providers

What if you need to perform some asynchronous operation before a provider is available? That’s where useFactory and the async keyword come in.

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

@Injectable()
export class AsyncService {
  async getValueAsync(): Promise {
    return 'Async Value';
  }
}

@Module({
  providers: [
    {
      provide: 'ASYNC_VALUE',
      useFactory: async (): Promise => {
        const asyncService = new AsyncService();
        return await asyncService.getValueAsync();
      },
    },
  ],
})
export class AppModule {}

Here the ASYNC_VALUE token is associated with a Promise resolved to a string, retrieved from an asynchronous service function.

Property-based Injection

Although constructor-based injection is more prevalent and recommended in NestJS, property-based injection can occasionally be useful. Below is an example of how it’s done:

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

@Injectable()
export class PropertyBasedService {
  @Inject('MY_VALUE')
  private readonly value: string;

  getValue(): string {
    return this.value;
  }
}

Note: Use property-based injection judiciously as it makes your dependencies less transparent.

Injecting Request Context

NestJS enables you to inject the current request context into your service, which is useful for scoped providers like request-scoped services.

import { Injectable, Scope, Inject } from '@nestjs/common';
import { REQUEST } from '@nestjs/core';
import { Request } from 'express';

@Injectable({ scope: Scope.REQUEST })
export class RequestContextService {
  constructor(@Inject(REQUEST) private request: Request) {}

  getRequestDetails() {
    return {
      method: this.request.method,
      url: this.request.url,
    };
  }
}

Using the @Inject() Decorator

The @Inject() decorator is most often used for custom injection tokens or for services not decorated with @Injectable(), such as third-party classes:

import { Injectable, Inject } from '@nestjs/common';
import { ThirdPartyService } from 'some-third-party-library';

@Injectable()
export class MyExtendedService {
  constructor(@Inject(ThirdPartyService) private thirdPartyService: ThirdPartyService) { }

  callThirdPartyFunction(): string {
    return this.thirdPartyService.performAction();
  }
}

Advanced Techniques

Understanding DI in NestJS is crucial for leveraging its full potential, especially when designing large scale applications. Integrating third-party services, creating hierarchical injectors, and using scope-based injection are parts of its advanced usage. The above examples set the basis for exploiting powerful and flexible DI in NestJS.

Conclusion

Utilizing Dependency Injection in NestJS allows developers to write modular, testable code with ease. From basic service injection to complex use cases involving custom providers, scope, and third-party libraries, mastering DI is essential for any NestJS developer. The examples provided should serve as a strong foundation for your NestJS DI adventures.

Next Article: NestJS: Configuration and Environment Variables

Previous Article: How to Use GraphQL with NestJS

Series: Nest.js Tutorials: From Basics to Advanced

Node.js

You May Also Like

  • NestJS: How to create cursor-based pagination (2 examples)
  • Cursor-Based Pagination in SequelizeJS: Practical Examples
  • MongooseJS: Cursor-Based Pagination Examples
  • Node.js: How to get location from IP address (3 approaches)
  • SequelizeJS: How to reset auto-increment ID after deleting records
  • SequelizeJS: Grouping Results by Multiple Columns
  • NestJS: Using Faker.js to populate database (for testing)
  • NodeJS: Search and download images by keyword from Unsplash API
  • NestJS: Generate N random users using Faker.js
  • Sequelize Upsert: How to insert or update a record in one query
  • NodeJS: Declaring types when using dotenv with TypeScript
  • Using ExpressJS and Multer with TypeScript
  • NodeJS: Link to static assets (JS, CSS) in Pug templates
  • NodeJS: How to use mixins in Pug templates
  • NodeJS: Displaying images and links in Pug templates
  • ExpressJS + Pug: How to use loops to render array data
  • ExpressJS: Using MORGAN to Log HTTP Requests
  • NodeJS: Using express-fileupload to simply upload files
  • ExpressJS: How to render JSON in Pug templates