question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

inject dependencies forRootAsync

See original GitHub issue

I am using the forRootAsync method to load roles dynamically from the database however I am getting an error. I am a newbie to Nestjs, so please let me know if I am doing something wrong

Error: Nest can't resolve dependencies of the __roles_builder__ (?). Please make sure that the argument at index [0] is available in the AccessControlModule context.

I am trying to to import the AccessControlModule in the root module and I have a RolesService to inject as a dependency in the forRootAsync method. The code is below

AccessControlModule.forRootAsync({
      inject: [{
        provide: ROLE_REPO,
        useClass: Role
      }],
      useFactory: (service: repo) => {
        return new RolesBuilder([
          ...adminGrants // testing local file grants
        ]);
      }
    })

Issue Analytics

  • State:open
  • Created 4 years ago
  • Comments:12 (5 by maintainers)

github_iconTop GitHub Comments

7reactions
thameracommented, Jan 4, 2021

Wish I could. I’m still learning a lot here and am not seeming to grasp some core elements here. I tried making the simple change directly in my local and it does work to be async but I still can’t get this to load roles/grants from my db. I ended up moving my roles data calls into their own module (just a standard CRUD module using mongoose. I tried importing that module into my access control module where this loads with an import of my roles module. Trying to inject the roles service into the forrootasync but keep getting errors about resolving dependencies. I’m thinking the accesscontrolmodule loads too early in this setup to access the other module/service? Below is some of where I’m at if anyone has a suggestion:

Roles Module

@Module({
  imports: [
    MongooseModule.forFeature([{name: 'role', schema: RoleSchema}]),
    RolesModule
  ],
  controllers: [RolesController],
  providers: [RolesService],
  exports: [
    RolesService,
    MongooseModule.forFeature([{name: 'role', schema: RoleSchema}])    
  ]
})
export class RolesModule {}

Roles Service:

@Injectable()
export class RolesService implements IRolesService {
    constructor(@InjectModel('role') private readonly roleModel: Model<IRole> ) {}

    async findAll(): Promise<IRole[]> {
        return await this.roleModel.find().exec();
    }
...

Access Control Module:

@Module({
  imports: [
    RolesModule,
    AccessControlModule.forRootAsync({
       inject: [RolesService],
       useFactory: async (service: RolesService) => {            //service: RolesService
           const roles = await service.findAll();
           let roleGrants = [];

           ... populate roleGrants array to roles/grants from db

           return new RolesBuilder([
               ...roleGrants
           ]);
       }
   })
  ],
  providers: [AccesscontrolService],
  exports: [
    //...AccesscontrolProviders
  ]
})
export class AccesscontrolModule {}
2reactions
bashleighcommented, Jan 4, 2021

Ok so! Firstly! Sorry it’s taken me nearly 2 years to reply. I’ve had an absolutely mental year and finally taken some time off to come back to reality. I did a quick test #49 and discovered that for some reason I added an import property to the forRootAsync method which I thought was weird. But turns out, that’s what fixes your issue!

Not entirely sure why a provider specified as a provider is not found but an imported module, I think (trying to remember some research I did nearly 2 years ago) nestjs creates the imported modules before creating the providers you’ve specified so a theAccessControlModule.forRootAsync static method would be called before your provider is created therefore, cannot find your specified provider! SO! I’d added an import property, which I guess was added to get around this issue! I added my module with the provider I wanted to the imports property and BOOM, it worked. This will solve your issue!

@Injectable()
    class TestProvider {
    }

    @Module({
      providers: [TestProvider],
      exports: [TestProvider],
    })
    class TestModule {

    }

  @Module({
      imports: [
        AccessControlModule.forRootAsync({
          imports: [TestModule],
          inject: [TestProvider],
          useFactory: (test: TestProvider): RolesBuilder => {
            return new RolesBuilder();
          },
        }),
      ],
    })
 export class ExampleModule {}

So! Given this example and your provided snippet, it should work like this

@Module({
 imports: [
   AccessControlModule.forRootAsync({
      imports: [RolesModule], // <=============== notice change here! 
      inject: [RolesService],
      useFactory: async (service: RolesService) => {            //service: RolesService
          const roles = await service.findAll();
          let roleGrants = [];

          ... populate roleGrants array to roles/grants from db

          return new RolesBuilder([
              ...roleGrants
          ]);
      }
  })
 ],
 providers: [AccesscontrolService],
 exports: [
   //...AccesscontrolProviders
 ]
})
export class AccesscontrolModule {}

This then ensures your RoleModule is created and exports your injectable provider BEFORE your factory is called.

Now to travel back in time and give this info to you 2 years ago!

Read more comments on GitHub >

github_iconTop Results From Across the Web

Nestjs wrapping external module in forRootAsync and inject ...
In the first one, we pass the factory provider as following: { provide: HTTP_MODULE_OPTIONS, useFactory: options.useFactory, inject: options.
Read more >
Dynamic modules | NestJS - A progressive Node.js framework
And of course, we'll use dependency injection to do it. ... dynamic modules that expose async methods ( registerAsync , forRootAsync , etc.) ......
Read more >
nest-winston - npm
Important: by doing this, you give up the dependency injection, meaning that forRoot and forRootAsync are not needed and shouldn't be used.
Read more >
Queues | NestJS 中文文档
You can easily add new Queue consumers to scale up the back-end task handling as the ... To begin using it, we first...
Read more >
Documentation | NestJS - A progressive Node.js framework
Like other factory providers, our factory function can be async and can inject dependencies through inject . TypeScript. MongooseModule.forRootAsync({ imports: ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found