Nest: Application crash when using barrel files (multi index.ts)

Created on 9 Oct 2018  路  18Comments  路  Source: nestjs/nest

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:

question 馃檶

Most helpful comment

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).

All 18 comments

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

@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...

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.

@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.

@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?

@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?

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.

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

@kamilmysliwiec

tried to update. No difference

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.

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...

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.

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!

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".

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

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

and do not close it again

But now it's closed 馃槂

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).

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.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

cdiaz picture cdiaz  路  3Comments

anyx picture anyx  路  3Comments

mishelashala picture mishelashala  路  3Comments

hackboy picture hackboy  路  3Comments

cojack picture cojack  路  3Comments