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

inject dependencies forRootAsync #17

Open
anitricks opened this issue May 19, 2019 · 12 comments
Open

inject dependencies forRootAsync #17

anitricks opened this issue May 19, 2019 · 12 comments
Assignees
Labels
help wanted Extra attention is needed

Comments

@anitricks
Copy link

anitricks commented May 19, 2019

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
        ]);
      }
    })
@shekohex shekohex added the help wanted Extra attention is needed label May 19, 2019
@bashleigh
Copy link
Contributor

bashleigh commented May 21, 2019

I think your inject token is incorrect? Try this

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

Let me know how that goes. I think that should be right? Inject takes an array of provider tokens? Not Providers themselves? Think the required type (without looking at the documentation/type itself) it's string, Symbol, Function, Provider Where Provider is Function, String, Symbol?

@anitricks
Copy link
Author

@bashleigh i tried putting the token as the string but its throwing me error that it wants it this way: { provide, useClass } I need to inject a TypeORM entity or a RoleService to fetch the roles from the DB

@anitricks
Copy link
Author

can someone provide an example of injecting a database connection into forRootAsync method?

@bashleigh
Copy link
Contributor

@anitricks Sorry been mega busy! Could you provide an example application?

@thamera
Copy link

thamera commented Jun 25, 2019

How is this async? The useFactory doesn't accept an async promise of rolesbuilder so I don't see how this can be served from a database which will always need to wait for an async promise. in the forRootAsync, would it work to change it to be something like the following?

static forRootAsync(options: {
        inject?: Provider[];
        useFactory: (...args: any) => Promise<RolesBuilder> | RolesBuilder;
    }): DynamicModule;

@bashleigh
Copy link
Contributor

@thamera oh yea, that was stupid of me. Wanna make a PR for it with test? Really busy sorry.

@thamera
Copy link

thamera commented Jun 28, 2019

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 {}

@shajeeck
Copy link

shajeeck commented Jul 7, 2019

@thamera any updates over a solution or an exmaple?

@creaux
Copy link
Contributor

creaux commented Nov 13, 2019

I just needed something what works. I have realised that usually when you are using external module for db management it is worth of it to have also imports option.

What about something like this https://github.com/creaux/nest-access-control/blob/2974fe2d0cdef35d031f64023cf3f6b403b0f72c/src/access-control.module.ts#L54

@bashleigh
Copy link
Contributor

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!

@rxst
Copy link

rxst commented Jul 18, 2022

Hi, all!
If I understand correctly at Nest.js we can't use providers in the forRootAsync function?
Because when I try to import the provider that provides all my configs in forRootAsync. That provider can't be injected into the Module for usage. Maby somebody has a solution for this issue or any ideas on how to move around it?

@cantoute
Copy link

Hi
I'm not sure...
One thing to look at is what version of nestjs you have.
Since v8 this package is no more needed.
Documentation describes how to implement castle, perhaps you want to have a look at it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
help wanted Extra attention is needed
Projects
None yet
Development

No branches or pull requests

8 participants