nestjs
nestjs copied to clipboard
`MikroOrm` or `EntityManager` injection from @mikro-orm/<driver> doesn't work
Describe the bug
Hello !
I noticed that MikroOrm or EntityManager cannot be injected by NestJs when imported from @mikro-orm/<driver>.
Here is a repro: https://github.com/mikro-orm/nestjs-realworld-example-app/compare/v6...xmuller:nestjs-realworld-example-app:v6
Stack trace
EntityManager injection in UserService:
Nest can't resolve dependencies of the UserService (UserRepository, ?). Please make sure that the argument SqlEntityManager at index [1] is available in the RootTestModule context.
Potential solutions:
- Is RootTestModule a valid NestJS module?
- If SqlEntityManager is a provider, is it part of the current RootTestModule?
- If SqlEntityManager is exported from a separate @Module, is that module imported within RootTestModule?
@Module({
imports: [ /* the Module containing SqlEntityManager */ ]
})
MikroOrm injection in TagController:
Nest can't resolve dependencies of the TagController (?, TagService). Please make sure that the argument MySqlMikroORM at index [0] is available in the RootTestModule context.
Potential solutions:
- Is RootTestModule a valid NestJS module?
- If MySqlMikroORM is a provider, is it part of the current RootTestModule?
- If MySqlMikroORM is exported from a separate @Module, is that module imported within RootTestModule?
@Module({
imports: [ /* the Module containing MySqlMikroORM */ ]
})
To Reproduce
Steps to reproduce the behavior:
- yarn test
Expected behavior
MikroOrm and EntityManager should be able to be injected.
Versions
| Dependency | Version |
|---|---|
| node | 18 |
| typescript | 5.0.4 |
| mikro-orm | 6.0.0-dev.62 |
| your-driver | MySql |
I know it's broken with the MikroORM class, but it should work fine with EntityManager I think (there would be probably lots of people complaining). I don't think it's about v6 changes, this didn't really changed in there.
Will have a closer look later, if you want to check it yourself, this is probably the right place to start with:
https://github.com/mikro-orm/nestjs/blob/master/src/mikro-orm.providers.ts#L39
"On my machine" it doesn't seems to work from master either : https://github.com/mikro-orm/nestjs-realworld-example-app/compare/master...xmuller:nestjs-realworld-example-app:master
It may be a side effect of an installation issue but it's hard to tell.
I use yarn version 1.22.19.
I got the same error when i tried to switch from npm to yarn. I was wondering if it is caused by duplicate packages, package hoisting or similar.
@zoltan-mihalyi @B4nan ran into a similar issue, where switching from yarn to npm seems to have resolved the issue.
In my case I was working on a shared library of modules so I could reuse them in other NestJS apps. They depended on MikroOrm/Entitymanager in the services. Very randomly when I would make updates to the shared library, then update the shared package in the main NestJS app I would get a dependency resolution issue with MikroOrm. Since MikroOrm is a global module, you'd think it should auto resolve so long as it's instantiated in the imports array of the Nest app before the shared library modules.
Still scratching my head on it a bit, was thinking perhaps using forRootAsync maybe means that MikroOrm isn't ready by the time the other Module is called... that said switching to npm seems to have solved it.... at least for now...
I have solved the duplicate package problem by specifying nested dependencies (@mikro-orm/knex and pg) as peer dependencies and adding them to my package.json. But I am not sure if this is the optimal solution.
.yarnrc.yaml
"@mikro-orm/cli@*":
peerDependencies:
"@mikro-orm/knex": "5.6.1"
"@mikro-orm/migrations@*":
peerDependencies:
"@mikro-orm/knex": "5.6.1"
"@mikro-orm/nestjs@*":
peerDependencies:
"@mikro-orm/knex": "5.6.1"
"@mikro-orm/postgresql@*":
peerDependencies:
"@mikro-orm/knex": "5.6.1"
"knex@*":
peerDependencies:
"pg": "*"
package.json
"dependencies": {
"@mikro-orm/knex": "5.6.1",
"pg": "8.8.0",
...
}
Hi, I did some digging into this issue. The problem lies in how NestJs resolves dependencies and what providers are provided by this package when, for example, the forRoot function is called.
Let's start with the NestJs part. If you call module.get<MikroORM>(MikroORM) (like it's done in tests of this library) this function is called to get the desired instance by token, line 33 in mentioned function attempts to find an instance based on the provided token.
So if you import MikroOrm from the core module the token will be the MikroOrm class. But if you import MikroOrm from the sqlite library the token will be the SqliteMikroORM class (which extends MikroOrm class), but as you can see here this library only provides MikroOrm class imported from the core package so that is why the NestJs fails to resolve the dependencies.
But why does it work for EntityManger (when using sqllite driver)? It's because both EntityManager and SqlEntityManager are provided. The EntitiyManager is registered as a provider by this line and SqlEntityManager is registered as a provider by this line, yes the knex.EntityManager is SqlEntityManager in this case.
How to fix it? So far I come up with two possible solutions:
- If we provide a string instead of
MikroOrmclass on this line (for the case when thecontextNameis nullish) and we use@Inject()from NestJs and use the same string, IMHO it should work. But it's very ugly, so I think we should avoid it. - We could update the
forRootandforRootAsyncfunctions in theMikroOrmCoreModule. In these functions, we could try to require all supported drivers likeconst mysql = await tryRequire('@mikro-orm/mysql'), and ifmysql?.MikroORMis true, we would register it. The registration could be done by adding another optional parameter to thecreateMikroOrmProvider. So this way bothMikroOrmandSqliteMikroORMshould be registered.
The second solution seems better, but I didn't test it. Can anyone think of a better solution?
I wasn't able to reproduce any problems with imports of driver-specific EM class (so if that's still happening to someone, complete repro welcome), #176 adds support for driver-specific MikroORM class imports. Will update the real-world example app to use driver-specific imports of everything once this is released.
Actually, I found one case where the EM support fails as well as its root cause. Should be addressed in v6.0.1 via a71c35418dc07e546f6060706cb38c3a0b1003aa.
Will update the real-world example app to use driver-specific imports of everything once this is released.
Done https://github.com/mikro-orm/nestjs-realworld-example-app/pull/111
Hello. The problem is still exists when using Async provider with injections
MikroOrmModule.forRootAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: (configService: ConfigService) => {
const highligher = new SqlHighlighter()
const debug = configService.get('NODE_ENV') === 'development'
return {
...defineConfig({
entities: [path.join(__dirname, './entities/main/*.js')],
entitiesTs: [path.join(__dirname, './entities/main/*.ts')],
strict: false,
forceUtcTimezone: true,
debug,
host: configService.get('DATABASE_HOST'),
port: +configService.get('DATABASE_PORT'),
user: configService.get('DATABASE_USER'),
password: configService.get('DATABASE_PASSWORD'),
dbName: configService.get('DATABASE_MAIN_DB'),
migrations: {
path: 'dist/modules/database/migrations/main',
pathTs: 'src/modules/database/migrations/main',
transactional: true,
allOrNothing: true,
},
highlighter: debug ? highligher : undefined,
extensions: [Migrator],
}),
//register our own OrmMiddleware below
registerRequestContext: false,
}
},
}),
I think the problem in this lines https://github.com/mikro-orm/nestjs/blob/606f9ca594829d5d7e14b6131b413e12a25390a9/src/mikro-orm-core.module.ts#L162-L164 There is options.useFactory() calls without injections and throw the Error
That's probably a wontfix, at least with the current implementation (and I can't personally think of a better approach, although there is likely one more "nest di native"). See #184 for possible workaround.