Nest js convert module from forRoot to forRootAsync

3.1k views Asked by At
@Module({
  imports: [],
  providers: [SupertokensService, AuthService],
  exports: [],
  controllers: [AuthController],
})
export class AuthModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer.apply(AuthMiddleware).forRoutes('*');
  }
  static forRoot({
    connectionURI,
    apiKey,
    appInfo,
  }: AuthModuleConfig): DynamicModule {
    return {
      providers: [
        {
          useValue: {
            appInfo,
            connectionURI,
            apiKey,
          },
          provide: ConfigInjectionToken,
        },
      ],
      exports: [],
      imports: [],
      module: AuthModule,
    };
  }
}

The problem with this implementaion I can't use env variables, so I need useFactory to pass ConfigService. Can somebody do that, and give some explanation.

2

There are 2 answers

0
Eivydas Vickus On BEST ANSWER

I figure out how to make this work, unfortunately it only works with nest.js version 9 (current latest version). First you need to create a new file. For example auth.module-definition.ts. Now in this file we need to create ConfigurableModuleBuilder.

import { ConfigurableModuleBuilder } from '@nestjs/common';
import { AuthModuleConfig } from './config.interface';

export const { ConfigurableModuleClass, MODULE_OPTIONS_TOKEN } =
  new ConfigurableModuleBuilder<AuthModuleConfig>()
    .setClassMethodName('forRoot')
    .build();

we need to set setClassMethodName('forRoot'), when we set forRoot it will create two methods, forRoot and forRootAsync. The next step is to extend our created ConfigurableModuleClass. it should look something like this

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

import { AuthMiddleware } from './auth.middleware';
import { SupertokensService } from './supertokens/supertokens.service';
import { AuthController } from './auth.controller';
import { AuthService } from './auth.service';
import { ConfigurableModuleClass } from './auth.module-definition';;

@Module({
  imports: [],
  providers: [SupertokensService, AuthService],
  controllers: [AuthController],
  exports: [AuthService],
})
export class AuthModule extends ConfigurableModuleClass {
  configure(consumer: MiddlewareConsumer) {
    consumer.apply(AuthMiddleware).forRoutes('*');
  }
}
And that's actually it, so from now on we have forRootAsync and we get reigister it in app.module.ts

import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
import { AuthModule } from './auth/auth.module';
import { ConfigModule, ConfigType } from '@nestjs/config';
import authConfig from './auth/auth.config';

@Module({
  imports: [
    AuthModule.forRootAsync({
      inject: [authConfig.KEY],
      imports: [ConfigModule.forFeature(authConfig)],
      useFactory: (config: ConfigType<typeof authConfig>) => {
        return {
          connectionURI: config.CONNECTION_URI,
          appInfo: {
            appName: config.appInfo.APP_NAME,
            apiDomain: config.appInfo.API_DOMAIN,
            websiteDomain: config.appInfo.WEBSITE_DOMAIN,
            apiBasePath: config.appInfo.API_BASE_PATH,
            websiteBasePath: config.appInfo.WEBSITE_BASE_PATH,
          },
        };
      },
    })
  ],
  controllers: [],
  providers: [
  ],
})
export class AppModule implements NestModule {
}

here I am using Nest.js Config, but you don't need to, so use it how you want. I know that my english is not the best, so if you still do not understand you can check these sources https://docs.nestjs.com/fundamentals/dynamic-modules#configurable-module-builder https://trilon.io/blog/nestjs-9-is-now-available#Configurable-module-builder

0
Julien L On

if you want to be compatible with v6 + and 9+ as well you can make a new provider.

import { MySql2Database } from 'drizzle-orm/mysql2';
import { DRIZZLE_ORM } from './constants';
import { NestDrizzleService } from './nest-drizzle.service';
import { PostgresJsDatabase } from 'drizzle-orm/postgres-js';
import { BetterSQLite3Database } from 'drizzle-orm/better-sqlite3';

export const connectionFactory = {
  provide: DRIZZLE_ORM,
  useFactory: async (nestDrizzleService: {
    getDrizzle: () => Promise<
      MySql2Database | PostgresJsDatabase | BetterSQLite3Database
    >;
  }) => {
    return nestDrizzleService.getDrizzle();
  },
  inject: [NestDrizzleService],
};
Provide is made to use a token (best practices):

@Inject(DRIZZLE_ORM) private readonly db: PostgresJsDb

And use it like:

public static forRootAsync(options: NestDrizzleAsyncOptions): DynamicModule {
    return {
      module: NestDrizzleModule,
      providers: [...this.createProviders(options)],
      exports: [...this.createProviders(options)],
    };
  }

  private static createProviders(options: NestDrizzleAsyncOptions): Provider[] {
    if (options.useExisting || options.useFactory) {
      return [this.createOptionsProvider(options)];
    }

    return [
      this.createOptionsProvider(options),
      {
        provide: options.useClass,
        useClass: options.useClass,
      },
    ];
  }