Skip to content

Commit bc7e337

Browse files
committed
feat: add driver option to get around issues with useFactory and 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 {} ```
1 parent 4e78c47 commit bc7e337

File tree

4 files changed

+105
-19
lines changed

4 files changed

+105
-19
lines changed

README.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,27 @@ export class PhotoService {
111111
}
112112
```
113113

114+
## Driver-specific imports
115+
116+
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.
117+
118+
Instead of relying on this discovery, you can provide the driver type explicitly:
119+
120+
```typescript
121+
@Module({
122+
imports: [
123+
MikroOrmModule.forRootAsync({
124+
useFactory: (configService: ConfigService) => configService.getOrThrow(ConfigKey.ORM),
125+
inject: [ConfigService],
126+
driver: PostgreSqlDriver,
127+
}),
128+
],
129+
controllers: [AppController],
130+
providers: [AppService],
131+
})
132+
export class AppModule {}
133+
```
134+
114135
## Auto entities automatically
115136

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

259281
@Module({
260282
imports: [

src/mikro-orm-core.module.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,31 @@ export class MikroOrmCoreModule implements NestModule, OnApplicationShutdown {
4949

5050
static async forRoot(options?: MikroOrmModuleSyncOptions): Promise<DynamicModule> {
5151
const contextName = this.setContextName(options?.contextName);
52+
53+
if (options?.driver && !contextName) {
54+
const packageName = PACKAGES[options.driver.name as keyof typeof PACKAGES];
55+
const driverPackage = await tryRequire(packageName);
56+
57+
if (driverPackage) {
58+
return {
59+
module: MikroOrmCoreModule,
60+
providers: [
61+
{ provide: MIKRO_ORM_MODULE_OPTIONS, useValue: options || {} },
62+
createMikroOrmProvider(contextName),
63+
createMikroOrmProvider(contextName, driverPackage.MikroORM),
64+
createEntityManagerProvider(options?.scope, EntityManager),
65+
createEntityManagerProvider(options?.scope, driverPackage.EntityManager),
66+
],
67+
exports: [
68+
MikroORM,
69+
EntityManager,
70+
driverPackage.EntityManager,
71+
driverPackage.MikroORM,
72+
],
73+
};
74+
}
75+
}
76+
5277
const knex = await tryRequire('@mikro-orm/knex');
5378
const mongo = await tryRequire('@mikro-orm/mongodb');
5479
const em = await this.createEntityManager(options);
@@ -100,6 +125,33 @@ export class MikroOrmCoreModule implements NestModule, OnApplicationShutdown {
100125

101126
static async forRootAsync(options: MikroOrmModuleAsyncOptions): Promise<DynamicModule> {
102127
const contextName = this.setContextName(options?.contextName);
128+
129+
if (options?.driver && !contextName) {
130+
const packageName = PACKAGES[options.driver.name as keyof typeof PACKAGES];
131+
const driverPackage = await tryRequire(packageName);
132+
133+
if (driverPackage) {
134+
return {
135+
module: MikroOrmCoreModule,
136+
imports: options.imports || [],
137+
providers: [
138+
...(options.providers || []),
139+
...createAsyncProviders({ ...options, contextName: options.contextName }),
140+
createMikroOrmProvider(contextName),
141+
createMikroOrmProvider(contextName, driverPackage.MikroORM),
142+
createEntityManagerProvider(options?.scope, EntityManager),
143+
createEntityManagerProvider(options?.scope, driverPackage.EntityManager),
144+
],
145+
exports: [
146+
MikroORM,
147+
EntityManager,
148+
driverPackage.EntityManager,
149+
driverPackage.MikroORM,
150+
],
151+
};
152+
}
153+
}
154+
103155
const knex = await tryRequire('@mikro-orm/knex');
104156
const mongo = await tryRequire('@mikro-orm/mongodb');
105157
const em = await this.createEntityManager(options);

src/typings.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,12 @@
1-
import type { AnyEntity, EntityName as CoreEntityName, EntitySchema, ForkOptions, IDatabaseDriver, Options } from '@mikro-orm/core';
1+
import type {
2+
AnyEntity,
3+
Constructor,
4+
EntityName as CoreEntityName,
5+
EntitySchema,
6+
ForkOptions,
7+
IDatabaseDriver,
8+
Options,
9+
} from '@mikro-orm/core';
210
import type { MiddlewareConsumer, ModuleMetadata, Scope, Type } from '@nestjs/common';
311
import type { AbstractHttpAdapter } from '@nestjs/core';
412

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

tests/mikro-orm.module.test.ts

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,7 @@ describe('MikroORM Module', () => {
199199
...testOptions,
200200
logger: logger.log.bind(logger),
201201
}),
202+
driver: SqliteDriver,
202203
inject: ['my-logger'],
203204
providers: [myLoggerProvider],
204205
})],
@@ -316,24 +317,26 @@ describe('MikroORM Module', () => {
316317
it('forRootAsync :useFactory', async () => {
317318
const module = await Test.createTestingModule({
318319
imports: [
319-
MikroOrmModule.forRootAsync({
320-
contextName: 'database1',
321-
useFactory: (logger: Logger) => ({
322-
...testOptions,
323-
logger: logger.log.bind(logger),
324-
}),
325-
inject: ['my-logger'],
326-
providers: [myLoggerProvider],
327-
}),
328-
MikroOrmModule.forRootAsync({
329-
contextName: 'database2',
330-
useFactory: (logger: Logger) => ({
331-
...testOptions,
332-
logger: logger.log.bind(logger),
333-
}),
334-
inject: ['my-logger'],
335-
providers: [myLoggerProvider],
336-
}),
320+
...MikroOrmModule.forRootAsync([
321+
{
322+
contextName: 'database1',
323+
useFactory: (logger: Logger) => ({
324+
...testOptions,
325+
logger: logger.log.bind(logger),
326+
}),
327+
inject: ['my-logger'],
328+
providers: [myLoggerProvider],
329+
},
330+
{
331+
contextName: 'database2',
332+
useFactory: (logger: Logger) => ({
333+
...testOptions,
334+
logger: logger.log.bind(logger),
335+
}),
336+
inject: ['my-logger'],
337+
providers: [myLoggerProvider],
338+
},
339+
]),
337340
],
338341
}).compile();
339342

0 commit comments

Comments
 (0)