Creating Dynamic Modules in Nest JS Part-1
Tags: nestjs, node, javascript
Published: 2022-03-31 15:47:36


## Creating Dynamic Modules in Nest JS Part-1

!['dynamic module nestjs'](https://res.cloudinary.com/practicaldev/image/fetch/s--IB8OV0JK--/c_imagga_scale,f_auto,fl_progressive,h_420,q_auto,w_1000/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5olr2l6hd1z1mrac7fab.jpg)
Code 
https://github.com/tkssharma/blogs/tree/master/nestjs-dynamic-module
This is really a hot topic in nestjs and there is not much content available on dynamic Module.

Blog Originally Published here https://tkssharma.com/nestjs-creating-dynamic-modules-part-1/

Lets unfold the mystery of dynamic modules step by step 
What is nestjs Module, something which we are writing in every nestjs projects 

```javascript
import { Module } from '@nestjs/common';
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';

@Module({
  controllers: [CatsController],
  providers: [CatsService],
})
export class CatsModule {}
```
Module is Just a collection of controllers, providers and exports and these modules can be shared and used by other Modules 
Lets say i have created sendGrid Module, AzureBlobModule or Database Module, These Module will be used by other Modules and sometime 
when we import these modules we also need to pass configuration like DatabaseModule will need DB connection Url, Azure Module may need Azure Connection 
details for Blob Upload 

Most of the times we do static Module Import Like UserModule importing Account Module and we are Importing Both in Root Module
we don't need to pass any configurations there 

```javascript
import { Module } from '@nestjs/common';
import { AuthService } from './auth.service';
import { UsersModule } from '../users/users.module';

@Module({
  imports: [UsersModule],
  providers: [AuthService],
  exports: [AuthService],
})
export class AuthModule {}
```
In this Example what if i want to configure UserModule based on the use case i have with Auth Module 
### Dynamic module use case
With static module binding, there's no opportunity for the consuming module to influence how providers from the host module are configured. Why does this matter? Consider the case where we have a general purpose module that needs to behave differently in different use cases. This is analogous to the concept of a "plugin" in many systems, where a generic facility requires some configuration before it can be used by a consumer.

et's consider what a dynamic module import, where we're passing in a configuration object, might look like. Compare the difference in the imports array between these two examples:
`ConfigModule` from nestjs is a dynamic Module as i can pass my own configuration before using this my module

```javascript
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { ConfigModule } from './config/config.module';

@Module({
  imports: [ConfigModule.register({ folder: './config' })],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}
```

- ConfigModule is a normal class, so we can infer that it must have a static method called register(). We know it's static because we're calling it on the ConfigModule class, not on an instance of the class. Note: this method, which we will create soon, can have any arbitrary name, but by convention we should call it either forRoot() or register().
- The register() method is defined by us, so we can accept any input arguments we like. In this case, we're going to accept a simple options object with suitable properties, which is the typical case.

Lets have a look how it looks like 

```javascript
import { DynamicModule, Module } from '@nestjs/common';
import { ConfigService } from './config.service';

@Module({})
export class ConfigModule {
  static register(): DynamicModule {
    return {
      module: ConfigModule,
      providers: [ConfigService],
      exports: [ConfigService],
    };
  }
}

import { Injectable } from '@nestjs/common';
import * as dotenv from 'dotenv';
import * as fs from 'fs';
import { EnvConfig } from './interfaces';

@Injectable()
export class ConfigService {
  private readonly envConfig: EnvConfig;

  constructor() {
    const options = { folder: './config' };

    const filePath = `${process.env.NODE_ENV || 'development'}.env`;
    const envFile = path.resolve(__dirname, '../../', options.folder, filePath);
    this.envConfig = dotenv.parse(fs.readFileSync(envFile));
  }

  get(key: string): string {
    return this.envConfig[key];
  }
}

```
In this above example `const options = { folder: './config' };` we are not using passed value from Module, we need to find a way to Use the path passed in 
ConfigModule.register({ folder: './config' } method 
what we need to do is define our options object as a provider. This will make it injectable into the ConfigService, which we'll take advantage of in the next step. In the code below, pay attention to the providers array:

```javascript
@Module({})
export class ConfigModule {
  static register(options): DynamicModule {
    return {
      module: ConfigModule,
      providers: [
        {
          provide: 'CONFIG_OPTIONS',
          useValue: options,
        },
        ConfigService,
      ],
      exports: [ConfigService],
    };
  }
}

import * as dotenv from 'dotenv';
import * as fs from 'fs';
import { Injectable, Inject } from '@nestjs/common';
import { EnvConfig } from './interfaces';

@Injectable()
export class ConfigService {
  private readonly envConfig: EnvConfig;

  constructor(@Inject('CONFIG_OPTIONS') private options) {
    const filePath = `${process.env.NODE_ENV || 'development'}.env`;
    const envFile = path.resolve(__dirname, '../../', options.folder, filePath);
    this.envConfig = dotenv.parse(fs.readFileSync(envFile));
  }

  get(key: string): string {
    return this.envConfig[key];
  }
}
// One final note: for simplicity we used a string-based injection token ('CONFIG_OPTIONS') above, but best practice is to define it as a constant (or Symbol) in a separate file, and import that file. For example:

export const CONFIG_OPTIONS = 'CONFIG_OPTIONS';

```

`@Inject('CONFIG_OPTIONS') private options` will be able to inject the options which contains the path of the config file

## Examples of Existing Dynamic Modules 

In this Example someonr already built the dynamic Module and we are using it by passing our option Object we are getting from Config Service 

```javascript
import { SendGridModule } from "@ntegral/nestjs-sendgrid";

@Global()
@Module({
  imports: [
    SendGridModule.forRootAsync({
      imports: [ConfigModule],
      inject: [ConfigService],
      useFactory: (config: ConfigService) => ({
        apiKey: config.get("SENDGRID_ACCESS_KEY") || "",
      }),
    }),
  ],
  providers: [SendgridService],
  exports: [SendgridService],
})
export class SendgridModule {}
```

Here we can see these Modules which are already available as NPM Module exposing forRoot and forRootAsync methods to dynamically initialize
these modules

```javascript
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';

@Module({
  imports: [
    TypeOrmModule.forRoot({
      type: 'mysql',
      host: 'localhost',
      port: 3306,
      username: 'root',
      password: 'root',
      database: 'test',
      entities: [],
      synchronize: true,
    }),
  ],
})
export class AppModule {}

// or  forRootAsync 
TypeOrmModule.forRootAsync({
  useFactory: async () =>
    Object.assign(await getConnectionOptions(), {
      autoLoadEntities: true,
    }),
});
```

## Custom Dynamic Module on @nestjs/typeorm

We can create Database Module which will Use TypeORM Module and will access configurations from config Module 

```javascript
@Module({})
export class DatabaseModule {
  private static getConnectionOptions(config: ConfigService, dbconfig: DbConfig): TypeOrmModuleOptions {
    const dbdata = config.get().db;
    if (!dbdata) {
      throw new DbConfigError('Database config is missing');
    }
    const connectionOptions = DbModule.getConnectionOptionsPostgres(dbdata);
    return {
      ...connectionOptions,
      entities: dbconfig.entities,
      synchronize: false,
      logging: false,
    };
  }

  public static forRoot(dbconfig: DbConfig): DynamicModule {
    return {
      module: DbModule,
      imports: [
        TypeOrmModule.forRootAsync({
          imports: [ConfigModule, AppLoggerModule],
          // eslint-disable-next-line @typescript-eslint/no-unused-vars
          useFactory: (configService: ConfigService, logger: Logger) => DbModule.getConnectionOptions(configService, dbconfig),
          inject: [ConfigService],
        }),
      ],
      controllers: [],
      providers: [DatabaseService],
      exports: [DatabaseService],
    };
  }
}
```
Later in root module we can call root static Method to initialize this module asynchronously 
```javascript
@Module({
  imports: [
    DbModule.forRoot({
      entities: [Entity1, Entity2],
    })
  ]
});
```
Or we can also do it in same Module the whole idea is to pass the database configuration from config Module, config service

```javascript
@Global()
@Module({
  imports: [
    SendGridModule.forRootAsync({
      imports: [ConfigModule],
      inject: [ConfigService],
      useFactory: (config: ConfigService) => ({
      entities: [Entity],
      synchronize: false,
      logging: config.get().db.logging,
      type: config.get().type
      url: config.get().db.url
      keepConnectionAlive: true,
      ssl: false
      }),
    }),
  ],
  providers: [],
  exports: [],
})
export class DatabaseModule {}
```

## Conclusion 

- All these examples are talking about what is the use case of dynamic Module and how to use it, like existing library @nestjs/typeorm, sendGridModule and many more
- In next part we can also create our own dynamic Module and use it in another Module, when i say our own module which will be same as @nestjs/typeorm, nestjs config Module exposing forRoot and forRootAsync methods to initialize module dynamically 

## References 

- https://docs.nestjs.com/fundamentals/dynamic-modules
- https://docs.nestjs.com/