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

Application crash when using barrel files (multi index.ts) #1181

Closed
alexandr2110pro opened this issue Oct 9, 2018 · 18 comments
Closed

Application crash when using barrel files (multi index.ts) #1181

alexandr2110pro opened this issue Oct 9, 2018 · 18 comments

Comments

@alexandr2110pro
Copy link

I'm submitting a...


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

Current behavior

Having

/* InvitationsModule
 *********************** */
import { Module } from '@nestjs/common';
import { InvitationComposerService, InvitationsService } from './services';
import { DatabaseModule } from '../database';
import { AuthModule } from '../auth';
import { MailModule } from '../mail';
import { invitationModelProvider } from './providers';

@Module({
  imports: [
    DatabaseModule.forRoot([invitationModelProvider]),
    AuthModule,
    MailModule,
  ],
  providers: [InvitationsService, InvitationComposerService],
  exports: [InvitationsService],
})
export class InvitationsModule {}

/* AuthModule
 *********************** */
import { Module } from '@nestjs/common';
import { ExternalAuthModule } from '../external-auth';

import { AuthService, ExternalAuthAdapterService } from './services';
import { AuthController } from './auth.controller';
import { AuthMiddleware } from './auth.middleware';

@Module({
  imports: [ExternalAuthModule],
  providers: [AuthService, ExternalAuthAdapterService, AuthMiddleware],
  controllers: [AuthController],
  exports: [AuthService],
})
export class AuthModule {}

where ExternalAuthModule and MailModule have no module imports,

I have this:

screenshot 2018-10-09 19 26 51

which leads to

Error: Nest cannot create the module instance. Often, this is because of a circular dependency between modules. Use forwardRef() to avoid it. (Read more https://docs.nestjs.com/advanced/circular-dependency.) Scope [TestModule -> InvitationsModule -> AuthModule]

Expected behavior

It should work

Minimal reproduction of the problem with instructions

Not sure. We've got nearly 50 modules and all are working fine.

And most of them actually use AuthModule as you might guess.

Only this one, that I've created recently can't load. Any ideas on what am I doing wrong? :)

What is the motivation / use case for changing the behavior?

Environment


Nest version: 5.0.1

Actually, i've got the same is on v4

 
For Tooling issues:
- Node version: 10.10.0  
- Platform:  Mac

Others:

@kamilmysliwiec
Copy link
Member

I would suggest updating all modules, there are no breaking changes between 5.0.1 and latest version.

@alexandr2110pro
Copy link
Author

@kamilmysliwiec I think, the problem is not there. It is something really weird.

Probably it is not directly related to the NestJS. I'll post it on the TS github.. But maybe somebody here can suggest where to look at...

What I've noticed is that if I debug my code, some ts imports are undefined..

And what is even more interesting, that in some cases they are undefined, in others - the same imports are defined. 😄

let's say I have this:

// accounts/index.ts
export * from './interfaces';
export * from './bindings';
export * from './schemas';
export * from './dto';

export { AccountsModule } from './accounts.module';
export { AccountsService } from './accounts.service';
import { Module } from '@nestjs/common';
import { DatabaseModule } from '../database';
import { MailModule } from '../mail';
import { invitationModelProvider } from './providers';
import { AccountsModule } from '../accounts';

console.log(AccountsModule, MailModule);

@Module({
  imports: [
    DatabaseModule.forRoot([invitationModelProvider]),
    AccountsModule,
    MailModule,
  ]
})
export class InvitationsModule {}

it logs

undefined [Function: MailModule]

but if I change it to import { AccountsModule } from '../accounts/accounts.module';

it logs [Function: AccountsModule] [Function: MailModule]

but have similar issues in some AccountsModule imports.

And I can't see any pattern in the bahaviour. E.g. a test for some module A can be green, but when testing a module B that depends on A, the imported A is undefined.

I'm confused. Not sure where to look at. We didn't change the ts configuration or anything like that. But several days ago it started failing.

Maybe some package dependencies resolved to the later versions and that caused all problems.

Trying to figure out...

@marcus-sa
Copy link

marcus-sa commented Oct 11, 2018

You need to wrap the AccountsModule in the forwardRef function.

import { Module, forwardRef } from '@nestjs/common';
import { DatabaseModule } from '../database';
import { MailModule } from '../mail';
import { invitationModelProvider } from './providers';
import { AccountsModule } from '../accounts';

@Module({
  imports: [
    DatabaseModule.forRoot([invitationModelProvider]),
    forwardRef(() => AccountsModule), // <-- here
    MailModule,
  ]
})
export class InvitationsModule {}

Circular dependencies are the reason why the imports are undefined.
if module A depends on module B which depends on module A it'll have a circular dependency.

@alexandr2110pro
Copy link
Author

alexandr2110pro commented Oct 11, 2018

@marcus-sa

No, there're no circular dependencies.

I know about forwardRef. It is not the case here.

Please check the prev. comment. The undefined is the ts import, not the NestJS import.

@alexandr2110pro
Copy link
Author

alexandr2110pro commented Oct 11, 2018

@marcus-sa look,

import { Module } from '@nestjs/common';
import { DatabaseModule } from '../database';
import { MailModule } from '../mail';
import { invitationModelProvider } from './providers';
import { AccountsModule } from '../accounts';

console.log(AccountsModule, MailModule);

@Module({
  imports: [
    DatabaseModule.forRoot([invitationModelProvider]),
    AccountsModule,
    MailModule,
  ]
})
export class InvitationsModule {}

See, where the console.log is?

it has nothing to do with forwardRef.

It is undefined. Then it goes as undefined to the nest's imports array.

Then, eventually, Scanner#reflectMetadata() returns this array with undefined module (see the screenshot. )

Where am I wrong?

Why did you downvote my prev comment?

@alexandr2110pro
Copy link
Author

alexandr2110pro commented Oct 11, 2018

@marcus-sa
I've already tried to wrap in forwardRef (just in case if I understand how all the things work terribly wrong) and it ended up with

Cannot destructure property `relatedModules` of 'undefined' or 'null'.

Because the function that was passed to forwardRef returns undefined.

Understand?

@marcus-sa
Copy link

marcus-sa commented Oct 11, 2018

Maybe you should provide a repository to that we can try to reproduce the error.
For me it seems like the issues are your index.ts files that exports everything from that specific directory module.
There was a similar issue to this on here, that got fixed by getting rid of one export in some index.ts that exposes the directory files.

@alexandr2110pro
Copy link
Author

I would suggest updating all modules, there are no breaking changes between 5.0.1 and latest version.

@kamilmysliwiec

tried to update. No difference

@alexandr2110pro
Copy link
Author

Maybe you should provide a repository to that we can try to reproduce the error.

Yep, sure. If I don't find the issue otherwise.

@alexandr2110pro
Copy link
Author

alexandr2110pro commented Oct 11, 2018

It's the weirdest thing I've ever seen.

I have a common test utils module

// some-path/utils/index.ts
export { createRandomId } from './create-random-id';
export { createUser } from './create-user';
export { createUsers } from './create-users';
export { createAccessToken } from './create-access-token';
export { createDefaultAccessToken } from './create-default-access-token';
export { findAllUsers } from './find-all-users';
export { findOneUser } from './find-one-user';
export { createAccount } from './create-account';
export { inviteUser } from './invite-user';
export { findOneRole } from './find-one-role';

apparently, one of such functions caused this issue.

all find-* functions look mostly the same

async function findOneInvitation(
  fixturesService: MongooseFixturesService,
  filters: { _id: string } | { email: string; account: string }
): Promise<Invitation> {
  return await fixturesService.findOne(InvitationsBindings.MODEL_NAME, filters);
}

And all works fine until I've added another one.

How is that possible...

@alexandr2110pro
Copy link
Author

Even more interesting, when I just moved the function from the separate file in utils/ dir to the invitation.spec.ts everything seems to be working fine.

@alexandr2110pro
Copy link
Author

alexandr2110pro commented Oct 11, 2018

So in the end, nothing has changed... I didn't remove/change the code. I've just moved 2 functions from separate files to the test file body. That's it!

And of course, the imports in the test file changed:

/* <...> */
import {
  createAccessToken,
  createAccount,
  createUser,
  // findOneInvitation,  <-- was here when I had an exception
  // findOneAccount,     <-- was here when I had an exception
  E2ETestingContainer,
  E2ETestingModulesFactory,
  findOneRole,
  findOneUser,
  FixtureLoader,
  FixtureLoaderConfig,
  JEST_E2E_TIMEOUT,
  MongooseFixturesService,
} from '../../testing';
/* <...> */

@kamilmysliwiec

I think we can close the issue if you want.
But if somebody can tell me what the hell is going on here, I'd greatly appreciate that!

@alexandr2110pro
Copy link
Author

For me it seems like the issues are your index.ts files that exports everything from that specific directory module.

Why do you think it might be an issue?

I remember in the Angular community it was a common pattern. They called it "barrels".

@BrunnerLivio
Copy link
Member

Seems like a dublicate of #825. I can confirm @alexandr2110pro behavior. I am absolutely clueless why this happens. It only occuree to me inside a Nest application context, which is super strange. I think we should track this issue somewhere and do not close it again. Maybe it is a bug of TypeScript or maybe of Nest. Nonetheless we should track it

@kamilmysliwiec
Copy link
Member

I remember in the Angular community it was a common pattern. They called it "barrels".

As far as I know, barrels are no longer recommended due to this issue. It is nothing related to nest itself

@alexandr2110pro
Copy link
Author

and do not close it again

But now it's closed 😃

@kamilmysliwiec kamilmysliwiec changed the title innerModule is undefined in DependenciesScanner.scanForModules Application crash when using barrel files (multi index.ts) Oct 16, 2018
@kamilmysliwiec
Copy link
Member

kamilmysliwiec commented Oct 16, 2018

I have changed the issue title to make this thread more descriptive and easier to find by other people. Anyway, I don't think that we have to track this, this error is not related to Nest itself. I would suggest omitting barrel files when it comes to module's providers/module class OR using them very carefully (for example, even if you create a barrel, you shouldn't use it from within the same module itself [only from outside] and when circular dependency may potentially appear).

@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