Skip to content

Commit

Permalink
feat: add driver option to get around issues with useFactory and …
Browse files Browse the repository at this point in the history
…`inject`

When you try to inject the `EntityManager` or `MikroORM` symbols exported from the driver package, Nest.js needs to be aware of those typed. In other words, those driver specific exports need to be specifically registered in the DI container. This module uses automated discovery of the driver type in order to do that, but it fails to work when you use `useFactory` which requires some dependencies.

Instead of relying on this discovery, you can provide the driver type explicitly:

```typescript
@module({
  imports: [
    MikroOrmModule.forRootAsync({
      useFactory: (configService: ConfigService) => configService.getOrThrow(ConfigKey.ORM),
      inject: [ConfigService],
      driver: PostgreSqlDriver,
    }),
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}
```
  • Loading branch information
B4nan committed Jan 28, 2025
1 parent 4e78c47 commit bc7e337
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 19 deletions.
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,27 @@ export class PhotoService {
}
```

## Driver-specific imports

When you try to inject the `EntityManager` or `MikroORM` symbols exported from the driver package, Nest.js needs to be aware of those typed. In other words, those driver specific exports need to be specifically registered in the DI container. This module uses automated discovery of the driver type in order to do that, but it fails to work when you use `useFactory` which requires some dependencies.

Instead of relying on this discovery, you can provide the driver type explicitly:

```typescript
@Module({
imports: [
MikroOrmModule.forRootAsync({
useFactory: (configService: ConfigService) => configService.getOrThrow(ConfigKey.ORM),
inject: [ConfigService],
driver: PostgreSqlDriver,
}),
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
```

## Auto entities automatically

Manually adding entities to the entities array of the connection options can be
Expand Down Expand Up @@ -255,6 +276,7 @@ export class AppModule {}
Or, if you're using the Async provider:
```typescript
import { Scope } from '@nestjs/common';
import { PostgreSqlDriver } from '@mikro-orm/postgresql';

@Module({
imports: [
Expand Down
52 changes: 52 additions & 0 deletions src/mikro-orm-core.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,31 @@ export class MikroOrmCoreModule implements NestModule, OnApplicationShutdown {

static async forRoot(options?: MikroOrmModuleSyncOptions): Promise<DynamicModule> {
const contextName = this.setContextName(options?.contextName);

if (options?.driver && !contextName) {
const packageName = PACKAGES[options.driver.name as keyof typeof PACKAGES];
const driverPackage = await tryRequire(packageName);

if (driverPackage) {
return {
module: MikroOrmCoreModule,
providers: [
{ provide: MIKRO_ORM_MODULE_OPTIONS, useValue: options || {} },
createMikroOrmProvider(contextName),
createMikroOrmProvider(contextName, driverPackage.MikroORM),
createEntityManagerProvider(options?.scope, EntityManager),
createEntityManagerProvider(options?.scope, driverPackage.EntityManager),
],
exports: [
MikroORM,
EntityManager,
driverPackage.EntityManager,
driverPackage.MikroORM,
],
};
}
}

const knex = await tryRequire('@mikro-orm/knex');
const mongo = await tryRequire('@mikro-orm/mongodb');
const em = await this.createEntityManager(options);
Expand Down Expand Up @@ -100,6 +125,33 @@ export class MikroOrmCoreModule implements NestModule, OnApplicationShutdown {

static async forRootAsync(options: MikroOrmModuleAsyncOptions): Promise<DynamicModule> {
const contextName = this.setContextName(options?.contextName);

if (options?.driver && !contextName) {
const packageName = PACKAGES[options.driver.name as keyof typeof PACKAGES];
const driverPackage = await tryRequire(packageName);

if (driverPackage) {
return {
module: MikroOrmCoreModule,
imports: options.imports || [],
providers: [
...(options.providers || []),
...createAsyncProviders({ ...options, contextName: options.contextName }),
createMikroOrmProvider(contextName),
createMikroOrmProvider(contextName, driverPackage.MikroORM),
createEntityManagerProvider(options?.scope, EntityManager),
createEntityManagerProvider(options?.scope, driverPackage.EntityManager),
],
exports: [
MikroORM,
EntityManager,
driverPackage.EntityManager,
driverPackage.MikroORM,
],
};
}
}

const knex = await tryRequire('@mikro-orm/knex');
const mongo = await tryRequire('@mikro-orm/mongodb');
const em = await this.createEntityManager(options);
Expand Down
11 changes: 10 additions & 1 deletion src/typings.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
import type { AnyEntity, EntityName as CoreEntityName, EntitySchema, ForkOptions, IDatabaseDriver, Options } from '@mikro-orm/core';
import type {
AnyEntity,
Constructor,
EntityName as CoreEntityName,
EntitySchema,
ForkOptions,
IDatabaseDriver,
Options,
} from '@mikro-orm/core';
import type { MiddlewareConsumer, ModuleMetadata, Scope, Type } from '@nestjs/common';
import type { AbstractHttpAdapter } from '@nestjs/core';

Expand Down Expand Up @@ -83,6 +91,7 @@ export interface MikroOrmModuleAsyncOptions<D extends IDatabaseDriver = IDatabas
useExisting?: Type<MikroOrmOptionsFactory<D>>;
useClass?: Type<MikroOrmOptionsFactory<D>>;
useFactory?: (...args: any[]) => Promise<Omit<MikroOrmModuleOptions<D>, 'contextName'>> | Omit<MikroOrmModuleOptions<D>, 'contextName'>;
driver?: Constructor<D>;
inject?: any[];
}

Expand Down
39 changes: 21 additions & 18 deletions tests/mikro-orm.module.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ describe('MikroORM Module', () => {
...testOptions,
logger: logger.log.bind(logger),
}),
driver: SqliteDriver,
inject: ['my-logger'],
providers: [myLoggerProvider],
})],
Expand Down Expand Up @@ -316,24 +317,26 @@ describe('MikroORM Module', () => {
it('forRootAsync :useFactory', async () => {
const module = await Test.createTestingModule({
imports: [
MikroOrmModule.forRootAsync({
contextName: 'database1',
useFactory: (logger: Logger) => ({
...testOptions,
logger: logger.log.bind(logger),
}),
inject: ['my-logger'],
providers: [myLoggerProvider],
}),
MikroOrmModule.forRootAsync({
contextName: 'database2',
useFactory: (logger: Logger) => ({
...testOptions,
logger: logger.log.bind(logger),
}),
inject: ['my-logger'],
providers: [myLoggerProvider],
}),
...MikroOrmModule.forRootAsync([
{
contextName: 'database1',
useFactory: (logger: Logger) => ({
...testOptions,
logger: logger.log.bind(logger),
}),
inject: ['my-logger'],
providers: [myLoggerProvider],
},
{
contextName: 'database2',
useFactory: (logger: Logger) => ({
...testOptions,
logger: logger.log.bind(logger),
}),
inject: ['my-logger'],
providers: [myLoggerProvider],
},
]),
],
}).compile();

Expand Down

0 comments on commit bc7e337

Please sign in to comment.