Search code examples
nestjslazy-loadingtypeorm

NestJS lazy loading a module importing TypeORM doesn't register "Connection" providers


I have a DatabaseModule that imports TypeORM and starts the db connections (cf. database.module.ts). I use that module on a crud module and everything works fine while registring the modules the classical way.

But when I lazy load that same module, it breaks and I get the "Can't resolve Connection" error.

"ERROR [ExceptionsHandler] Nest can't resolve dependencies of the ProductRepository (?). Please make sure that the argument Connection at index [0] is available in the TypeOrmModule context."

(I'm writing serverless functions, hence the lazy-loading attempt.)

// product.module.ts: the module I lazy-load
@Module({
imports: [
  DatabaseModule.register({
    entities: [Product],
  }),
],
// database.module.ts: the module that creates the db connections
export interface DatabaseOptions {
  entities: any[];
}

@Module({})
export class DatabaseModule {
  static register(options: DatabaseOptions): DynamicModule {
    return {
      module: DatabaseModule,
      imports: [
        TypeOrmModule.forRoot({
          type: 'mysql',
          host: '0.0.0.0',
          port: 3306,
          username: 'root',
          password: '****',
          database: 'example',
          retryAttempts: 5,
          synchronize: true,
          entities: options.entities,
        }),
        TypeOrmModule.forFeature(options.entities),
      ],
      providers: [],
      exports: [TypeOrmModule],
    };
  }
}

Here is the lazy loading part.

// app.controller.ts
@Get()
async createProduct(): Promise<Product> {
  // Lazy load the module that imports the DatabaseModule to lazy-load the connections.
  const { ProductModule } = await import('./product/product.module');
  const productModule = await this.lazyModuleLoader.load(() => ProductModule);
  const { ProductService } = await import('./product/product.service');
  // It breaks here, 'Connection' providers are not registered.
  const productService = productModule.get(ProductService);

  return productService.create();
}

Once again, it works fine when I register the ProductModule in the main AppModule, so I expected the module to work the same way with lazy-loading.

I guess I have to lazy load more stuff for this to work, but I can't understand why the Connection / Repository providers are not available when I lazy-load the product module... 😦

Reproduction repo (There is a docker-compose with a mysql image to debug locally).


Solution

  • I faced the same problem, unfortunately. I've got it working via the ASYNC_CONNECTION provider:

    import { ConnectionOptions, createConnection, getConnection } from 'typeorm';
    
    providers: [{
      provide: 'ASYNC_CONNECTION',
      inject: [ConfigService],
      useFactory: async (configService: ConfigService) => {
        const { host, port, username, password, database } = configService.get('db');
    
        const connection: ConnectionOptions = {
          type: 'postgres',
          host,
          port,
          username,
          password,
          database,
          name: 'custom',
          entities: [...entities],
          synchronize: false,
          namingStrategy: new SnakeNamingStrategy(),
        };
    
        try {
          getConnection(connection.name);
        } catch (error) {
          return await createConnection(connection);
        }
      }
    }]
    

    Then you can inject the ASYNC_CONNECTION provider wherever you want:

    @Inject('ASYNC_CONNECTION')
    private connection: Connection,
    

    And use the classic repository approach with the entities you've declared above:

    this.connection.getRepository(<Entity>).findOne({})
    

    That's it.