# Deep Dive NestJS Scoping

Untuk pengguna NestJS salah satu feature ini cukup berguna untuk dipahami, khususnya pengguna awal yang baru pindah sebagai contoh dari Express ke NestJS. Menurut kepercayaan saya, dengan memahami feature ini, programmer bisa lebih bijak dalam menggunakan memory. Lalu kenapa saya menggunakan istilah “berguna” dibanding “sangat perlu”. Sebab programmer masih bisa menggunakan alternative dengan merancang secara manual (merancang mandiri).

Module-module yang menggunakan decorator `Injectable` di NestJS secara default akan membuat [singleton](https://verri.andriawan.web.id/singleton-pattern) `instance`. Dimana ketika pertama kali sebuah module provider di-*request* untuk di-*inject* ke constructor module, NestJS akan membuat satu `instance` dari module tersebut. Lalu instance tersebutakan dibungkus oleh Module `InstanceWrapper` dan disimpan juga dibiarkan tetap ada di `Heap` selama lifecycle app masih dalam kondisi hidup. Sehingga ketika ada module yang perlu menggunakannya lagi, NestJS tidak akan membuat instance baru, tapi memberikan instance yang sama, yang sebelumnya sudah dibuat (Singleton).

Berikut adalah garis besar flow ketika pertama kali NestJS dijalankan.

![](https://cdn.hashnode.com/uploads/covers/5cf3ce39e9d2e9950f3e2576/b9b670c8-71fb-4e88-b912-a4b2e6d6e98c.jpg align="center")

## **Injection Scope Type NestJS**

Pada NestJS terdapat tiga Scope Type yang bisa diimplementasikan.

### DEFAULT (Singleton)

Setiap Module dengan Scope `DEFAULT`, akan dibuat menjadi instance ketika NestJS di-inisialisasi atau ketika pertama kali NestJS App dijalankan bersamaan pada saat NestJS menginisialisasi `NestContainer`.

```typescript
// nest/packages/core/nest-factory.ts
export class NestFactoryStatic {
    public async create<T extends INestApplication = INestApplication>(
        moduleCls: IEntryNestModule,
        serverOrOptions?: AbstractHttpAdapter | NestApplicationOptions,
        options?: NestApplicationOptions,
  ): Promise<T> {

     // ...

     const applicationConfig = new ApplicationConfig();
     const container = new NestContainer(applicationConfig, appOptions);
     const graphInspector = this.createGraphInspector(options!, container);

     // ...

     await this.initialize(
      moduleCls,
      container,
      graphInspector,
      applicationConfig,
      options,
    );

    // ...
    
  }

  private async initialize(
    module: any,
    container: NestContainer,
    graphInspector: GraphInspector,
    config = new ApplicationConfig(),
    options: NestApplicationContextOptions = {},
    httpServer: HttpServer | null = null,
  ) {

    // ...

    try {

      await ExceptionsZone.asyncRun(
        async () => {
          await dependenciesScanner.scan(module);
          await instanceLoader.createInstancesOfDependencies();
          dependenciesScanner.applyApplicationProviders();
        },
        teardown,
        this.autoFlushLogs,
      );
    } catch (e) {
      this.handleInitializationError(e);
    }
  }
}
```

Terlihat pada snippet code di atas, pada method `initialize`, module akan scanning semua module ( `dependenciesScanner.scan` ) lalu dilanjutkan untuk pembuatan instance tiap-tiap module yang memiliki attribute scope `DEFAULT` ( `instanceLoader.createInstacesOfDependencies` ). Jika tidak ada masalah lanjut pada tahap apply semua providers (`dependenciesScanner.applyApplicationProviders`).

Ketika ada module lain yang memerlukan module ini, semua module yang diimport akan reference ke instance yang sama atau tanpa membuat instance baru (Singleton). Instance-instance ini akan tetep berada di heap selama NestJS tetap hidup.

### REQUEST

Pertama kali diinisialisasi, Nest App akan membuat instance `NestContainer`. Lalu Nest akan men-*scan* semua module-module yang terdaftar ( Seperti pada gambar flow di atas ). Setiap module yang ter-*capture* akan dibungkus (Wrapped) oleh `InstanceWrapper`. Berbeda dengan scope `DEFAULT`, NestJS tidak membuat instance pada saat initialisasi, tapi membuat instance pada saat request masuk.

Setiap kali request masuk ke presenter (ex: Controller) diterima oleh NestJS, NestJS akan mencari module mana yang memiliki Scope dengan tipe `Scope.REQUEST` . Jika ditemukan maka NestJS akan men-*generate* Random number sebagai Context ID dan membuat Instance setiap module yang ditemukan.

```typescript
// nest/packages/core/injector/instance-wrapper.ts
export interface ContextId {
  readonly id: number;
  payload?: unknown;
  getParent?(info: HostComponentInfo): ContextId;
}

// nest/packages/core/helpers/context-id-factory.ts
export function createContextId(): ContextId {
  return { id: Math.random() };
}
```

Selanjutnya `ContextID` yang sudah dibuat digunakan sebagai flag/key yang akan di push ke InstanceWrapper values bertipe `WeakMap` dengan value instance dari module tersebut.

```typescript
// nest/packagaes/core/injector/instance-wrapper.ts
export class InstanceWrapper<T = any> {
  public readonly name: any;
  public readonly token: InjectionToken;

  // ...

  private readonly values = new WeakMap<ContextId, InstancePerContext<T>>();

  // ...

}
```

Jadi setiap kali ada module yang melakukan permintaan untuk men-*inject* Module-module ini. Nest akan men-*inject* dari instance yang sudah dibuat ketika request masuk. Tidak membuat instance baru selama masih dalam satu lifecycle (request). Instance-instance ini tidak akan di `Heap` selamanya. Garbage Collector akan menghapusnya ketika sudah tidak ada yang menggunakan instance tersebut ( **WeakMap** ).

### TRANSIENT

Module dengan jenis Scope TRANSIENT ini, sama seperti pada Scope `DEFAULT` . Akan dibuat menjadi instance ketika NestApp diinisialisasi setelah `NestContainer` dibuat. Hanya saja yang membedakannya adalah setiap kali module ini di-*inject* ke module lain yang membutuhkannya. NestJS akan membuat instance sendiri-sendiri untuk masing-masing module yang meng-*import*\-nya. Instance-instance ini akan tetap di `Heap` selama app NestJS tetap hidup.

## Kesimpulan

Scope Default dan Transient secara lifecycle terlihat mirip. Keduanya dibuat ketika Nest App menginisialisasi `NestContainer`. Keduanya juga tetap di **Heap** selama Nest App Hidup (Running). Yang membedakan diantaranya yaitu Scope `DETAULT` hanya membuat satu instance untuk berapapun module yang meng-*import*\-nya. Sedangkan untuk Scope `TRANSIENT` akan dibuat instance Singleton sendiri-sendiri untuk tiap module yang meng-*import*\-nya.

Untuk Scope REQUEST, instance akan dibuat ketika request datang dan instance akan sementara berada di **Heap** (WeakMap Type). Selanjutnya Garbage Collector yang akan membersihkannya dari **Heap.**

Dari bagaimana cara NestJS menyimpan Instance-instance tersebut di memory, programmer akan lebih bisa bijak bagaimana merancang architecture dari program-nya.

## References

*   Github ([https://github.com/nestjs/nest](https://github.com/nestjs/nest))
    
*   NestJS ([https://docs.nestjs.com/fundamentals/injection-scopes](https://docs.nestjs.com/fundamentals/injection-scopes))
    
*   Singleton Pattern ([https://verri.andriawan.web.id/singleton-pattern](https://verri.andriawan.web.id/singleton-pattern))
