Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Defer init of TypeORM import (async dynamic modules) #800

Closed
j-maas opened this issue Jun 21, 2018 · 11 comments
Closed

Defer init of TypeORM import (async dynamic modules) #800

j-maas opened this issue Jun 21, 2018 · 11 comments

Comments

@j-maas
Copy link
Contributor

j-maas commented Jun 21, 2018

I'm submitting a...


[ ] Regression 
[ ] Bug report
[ ] Feature request
[x] Documentation issue or request
[x] Support request => Please do not submit support request here, instead post your question on Stack Overflow.

I tried asking this question on StackOverflow, but it got ignored and now I don't think I'll get any answers. I hope that me posting here is ok.

Expected behavior

I would like to defer the initialization of the TypeORM connection with TypeOrmModul.forRoot() until an in-memory mongodb is started using mongo-unit. I could imagine something like async module imports, but they do not work, and I can't figure out if I can use async providers or what to do otherwise.

What I tried

  • using a promise in the import statement: imports: [Promise.resolve(TypeOrmModule.forRoot(...))]
  • making the AppModule dynamic:
    @Module({})
    export class AppModule {
      static async withMongoUnit() {  // this does not work, it's just an idea
      const dbUrl = new URL(await mongoUnit.start());
        return {
          imports: [
            TypeOrmModule.forRoot({
              type: 'mongodb',
              host: dbUrl.host,
              port: dbUrl.port,
              database: 'test',
              entities: [__dirname + '/**/*.entity{.ts,.js}'],
              synchronize: true,
            }),
          ],
        };
      }
    }

Minimal reproduction of the problem with instructions

Create a module that imports a TypeOrmModule. Try to delay the initialization until after a promise resolves (in my case mongoUnit.start()).

// app.test.module.ts

import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import * as mongoUnit from 'mongo-unit';

@Module({})
export class AppModule {
  static async withMongoUnit() {  // this does not work, it's just an idea
    const dbUrl = new URL(await mongoUnit.start());
    return {
      imports: [
        TypeOrmModule.forRoot({
          type: 'mongodb',
          host: dbUrl.host,
          port: dbUrl.port,
          database: 'test',
          entities: [__dirname + '/**/*.entity{.ts,.js}'],
          synchronize: true,
        }),
      ],
    };
  }
}
// main.ts
...
async function bootstrap() {
  const app = await NestFactory.create(await AppModule.withMongoUnit());
  await app.listen(3000);
}

Environment


Nest version: 5.0.1
@kamilmysliwiec
Copy link
Member

That's impossible. I'd suggest initializing Mongo before creating app instance.

@j-maas
Copy link
Contributor Author

j-maas commented Jun 21, 2018

Is there a technical reason why this doesn't work or is it simply currently not implemented?

@cojack
Copy link
Contributor

cojack commented Jun 26, 2018

@y0hy0h modules are initialized in definition order, so if you're going to create an MongoModule where you will have an async provider, that will do your job to bring up mongo in memory, then you will be able to use it wherever you wanna.

@j-maas
Copy link
Contributor Author

j-maas commented Jun 26, 2018

So I could create a module that wraps TypeOrmModule and sets it up how I want, while having the same exports? (Basically, is there a way to replace TypeOrmModule with something, such that it's async but all its users don't have to change?)

I might have a closer look at how exactly async providers interact later. Maybe I missed something.

Thanks for your reply, @cojack!

@cojack
Copy link
Contributor

cojack commented Jun 27, 2018

@y0hy0h I have some boilerplate for nest with some additional stuff that Im using to start working on new project every time and Im trying to improve it if I feel that it needs some additional polishing. You can take a look at https://github.com/neoteric-eu/nestjs-auth
But Im not using a TypeOrmModule package, just create my own DatabaseModule where I perform connection to the Mongo, so, if you will create an additional provider there lets assume it will be called MongoMemory and then you will use it as dependency for DB_CON_TOKEN provider it will wait till your mongo memory database will be available. But rather doing that, I will follow @kamilmysliwiec thoughts about how it should be initialised.

Please consider to exclude mongo bootstrap from code and place it in the script that will bring your environment up, fe: docker-compose, also in the repo I've made, there is available docker-compose.yml file for development purpose to work with postgresql (I'm RDBMS lover ❤️) Take a look!

Regards!

@j-maas
Copy link
Contributor Author

j-maas commented Jun 27, 2018

Thanks, @cojack, for your answer! I appreciate your boilerplate. Still, I would prefer to reuse as much stuff, and write as little own code, as possible. That's why I'm pushing a bit to consider this in Nest.

Maybe in my use case it's best to take care of this outside the app. But more generally, wouldn't async dependencies be useful?

What I can do now, if I understood correctly, is write a module that depends on an async module, like e. g. TypeOrmModule, and then does its initialization after TypeOrmModule is started. But my use case is slightly different, in that I want to wrap TypeOrmModule to do additional work before it is initialized.
What is missing for this use case is basically a way to asynchronously create a dynamic module, i. e. call TypeOrmModule.forRoot() in an async function. For now, dynamic modules have to be created synchronously, and I wondered whether that is necessary.

I would totally understand if this is either technically impossible or would make the implementation too complicated. That was just idea I had and I think it makes sense in general, so I wanted to propose and discuss it.

@kamilmysliwiec
Copy link
Member

kamilmysliwiec commented Jun 27, 2018

What is missing for this use case is basically a way to asynchronously create a dynamic module, i. e. call TypeOrmModule.forRoot() in an async function. For now, dynamic modules have to be created synchronously, and I wondered whether that is necessary.

This feature will be available in the upcoming release.

@j-maas
Copy link
Contributor Author

j-maas commented Jun 27, 2018

Wow, that is amazing! :) Thanks for your quick reply!

@j-maas j-maas changed the title Defer init of TypeORM import Defer init of TypeORM import (async dynamic modules) Jun 27, 2018
@kamilmysliwiec
Copy link
Member

Nest 5.1.0 is published already

@dkushner
Copy link

dkushner commented Dec 2, 2018

This feature seems like exactly what I'm looking for in my efforts to establish consistent e2e testing without the need to mock repositories or stand up a test database. I'm trying to override my TypeORM module provider to instead use a SQLite in-memory database instance to back up my repositories. @y0hy0h @kamilmysliwiec Do you perhaps have an example of how you've used this to override the TypeORM configuration for testing? I've tried every permutation I can think of (including those mentioned in this and related threads) but I have been unable to do so.

@lock
Copy link

lock bot commented Sep 24, 2019

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

@lock lock bot locked as resolved and limited conversation to collaborators Sep 24, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

4 participants