DEV Community

Cover image for Dependency Injection Scopes In NestJS
İbrahim Gündüz
İbrahim Gündüz

Posted on • Originally published at Medium

Dependency Injection Scopes In NestJS

NestJS is one of the best server-side framework alternatives with the features it presents. Today we’re going to mention some secret power of the IoC container that comes out of the box with NestJS.

Normally, the framework manages providers' lifetime as singleton. In other words, having stateful objects in the container may create some issues. Let’s take a look at the following example:

// src/http-client.ts
import { Injectable } from "@nestjs/common";

@Injectable()
export class HttpClient {
 //...
 public setDefaultBaseUrl(baseUrl: string): void {
  this.baseUrl = baseUrl;
 }
 //...
}

// src/payment-service.ts
//...

@Injectable()
export class PaymentService {
 public constructor(
  private readonly client: HttpClient,
  @Inject('payment-service.url') private readonly serviceUrl: string,
 ) {
  this.client.setDefaultBaseUrl(serviceUrl);
 }
 //...
}

// src/delivery-service.ts
//...

@Injectable()
export class DeliveryService {
 public constructor(
  private readonly client: HttpClient,
  @Inject('delivery-service.url') private readonly serviceUrl: string,
 ) {
  this.client.setDefaultBaseUrl(serviceUrl);
 }
 //...
}

Enter fullscreen mode Exit fullscreen mode

So, if you have such a class like HttpClient above, once setDefaultBaseUrl() method is called, it would change whole other services' behavior dependent on itself.

Fortunately, NestJS allows you to define injection scopes on your custom providers.

Injection Scopes

Injection scope is a mechanism that allows the framework to take a decision about the instantiation strategy of the providers. A provider can have one of the following scopes:

DEFAULT: Once the application has bootstrapped, one single instance of each provider has been instantiated and shared across the application. Singleton scope is used by default.

TRANSIENT: Each provider that takes a transient scoped provider receives a new, dedicated instance of the provider. The created instances are not shared across the application.

REQUEST: It creates a new instance of the provider for each incoming request. The instance is garbage-collected after the request has completed processing.

Let’s get back to the previous example again.

Considering we have classes like DeliveryService and PaymentService dependent on a stateful HttpClient class, defining TRANSIENT scope for HttpClient would be the right injection scope.

// src/http-client.ts
import { Injectable, Scope } from "@nestjs/common";

@Injectable({ scope: Scope.TRANSIENT })
export class HttpClient {
 //...
 public setDefaultBaseUrl(baseUrl: string): void {
  this.baseUrl = baseUrl;
 }
 //...
}

// src/payment-service.ts
//...

@Injectable()
export class PaymentService {
 public constructor(
  private readonly client: HttpClient,
  @Inject('payment-service.url') private readonly serviceUrl: string,
 ) {
  this.client.setDefaultBaseUrl(serviceUrl);
 }
 //...
}

// src/delivery-service.ts
//...

@Injectable()
export class DeliveryService {
 public constructor(
  private readonly client: HttpClient,
  @Inject('delivery-service.url') private readonly serviceUrl: string,
 ) {
  this.client.setDefaultBaseUrl(serviceUrl);
 }
 //...
}
Enter fullscreen mode Exit fullscreen mode

Injection scopes is a fairly large subject and I strongly recommend you take a look at the framework documentation that covers more complex cases.

Thanks for reading.

Top comments (0)