Cannot inject custom provider for dynamic module - nestjs

I tried to create a dynamic module PermissionModule like the following:
permTestApp.module.ts
#Module({
imports: [PermissionModule.forRoot({ text: 'abc' })],
providers: [],
controllers: [PermissionTestController],
})
export class PermissionTestAppModule {}
permission.module.ts
import { DynamicModule, Module } from '#nestjs/common'
import { PermissionGuard } from './guard/permission.guard'
#Module({})
export class PermissionModule {
public static forRoot(config: { text: string }): DynamicModule {
return {
module: PermissionModule,
providers: [
{
provide: 'xoxo',
useValue: config.text,
},
PermissionGuard,
],
exports: [PermissionGuard],
}
}
}
permission.guard.ts
import {
CanActivate,
ExecutionContext,
Inject,
Injectable,
} from '#nestjs/common'
#Injectable()
export class PermissionGuard implements CanActivate {
constructor(#Inject('xoxo') private readonly config: string) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
console.log(31, this.config)
return true
}
}
AFAIK, 'abc' string must be injected when PermissionGuard is used.
I tried to test it with the following code.
permission.e2e.spec.ts
beforeAll(async () => {
const moduleRef: TestingModule = await Test.createTestingModule({
imports: [PermissionTestAppModule],
})
.compile()
app = moduleRef.createNestApplication()
controller = await moduleRef.resolve(PermissionTestController)
await app.init()
})
but it says,
Nest can't resolve dependencies of the PermissionGuard (?). Please make sure that the argument xoxo at index [0] is available in the PermissionTestAppModule context.
Potential solutions:
- Is PermissionTestAppModule a valid NestJS module?
- If xoxo is a provider, is it part of the current PermissionTestAppModule?
- If xoxo is exported from a separate #Module, is that module imported within PermissionTestAppModule?
#Module({
imports: [ /* the Module containing xoxo */ ]
})
What am I doing wrong?

Related

NestJS share dynamic module instance

I'm struggling a bit with Nest's dynamic modules.
I have a utility service which needs a password to be injected on startup.
#Injectable()
export class CryptoService {
constructor(
#Inject("password") private password: string
) {}
...
}
This service is part of a "shared" CryptoModule...
#Module({
providers: [CryptoService]
,exports: [CryptoService]
})
export class CryptoModule {
public static forRoot(password: string): DynamicModule {
const providers = [{
provide: "password"
,useValue: password
}];
return {
module: CryptoModule
,providers: providers
};
}
}
With "shared" I mean that it's not specific to the current app, so it can be reused in other Nest apps as well.
I import the CryptoModule in my AppModule like so...
#Module({
imports: [
...
,CryptoModule.forRoot("super-secure-password")
,EmailModule
]
,controllers: [...]
,providers: [...]
,exports: [CryptoModule]
})
export class AppModule {
...
}
This works and I can inject the CryptoService in my AppModule classes/services/controllers.
Now I have a 3rd Module (EmailModule) which should be independent from my app, so I can reuse it in other Nest apps as well.
Question now is, how do I import the CryptoModule in the EmailModule without having to set the password in the EmailModule? It should use the password that was passed to the CryptoModule in AppModule.
I tried importing CryptoModule like so...
#Module({
imports: [
...
,CryptoModule
]
,providers: [EmailService]
,exports: [EmailService]
})
export class EmailModule {
...
}
This code however triggers an exception on startup saying:
Nest can't resolve dependencies of the CryptoService...
If password is a provider, is it part of the current CryptoModule?
Is there a best practice of how to achieve this? I could make EmailModule dynamic as well and pass in the password, so it can forward it to the CryptoModule, but somehow that doesn't feel right.
Thanks!
I ended up doing the following (probably there are better ways of doing this)...
Added a static forChildren() function to the CryptoModule, which is used to initialize the module when being imported by the EmailModule (or any other module). The forRoot() method stores the password in an Observable, which is used by forChildren() to asynchronously retrieve the password. Doing this async is required to defeat the race condition between forChildren() and forRoot().
import { firstValueFrom, ReplaySubject } from 'rxjs';
...
#Module({
providers: [CryptoService]
,exports: [CryptoService]
})
export class CryptoModule {
private static readonly credentialsResolved = new ReplaySubject<string>(1);
public static forRoot(password: string): DynamicModule {
CryptoModule.credentialsResolved.next(password);
const providers = [{
provide: "password"
,useValue: password
}];
return {
module: CryptoModule
,providers: providers
};
}
public static forChildren(): DynamicModule {
const providers = [{
provide: "password"
,useFactory: async () => await firstValueFrom(CryptoModule.credentialsResolved)
}];
return {
module: CryptoModule
,providers: providers
};
}
}
Usage in EmailModule:
#Module({
imports: [
...
,CryptoModule.forChildren()
]
,providers: [EmailService]
,exports: [EmailService]
})
export class EmailModule {
...
}

NestJS - How to register dynamic module provider multiple times using different configuration?

I have a knex module which is implemented like this:
import { DynamicModule, Module } from '#nestjs/common';
import { Knex, knex } from 'knex';
import { WINSTON_MODULE_PROVIDER } from 'nest-winston';
import { Logger } from 'winston';
export const KNEX_MODULE = 'KNEX_MODULE';
#Module({})
export class KnexModule {
static register(options: Knex.Config): DynamicModule {
return {
module: KnexModule,
providers: [
{
inject: [WINSTON_MODULE_PROVIDER],
provide: KNEX_MODULE,
useFactory: (logger: Logger) => {
logger.info('Creating new knex instance', {
context: KnexModule.name,
tags: ['instance', 'knex', 'create'],
});
return knex(options);
},
},
],
exports: [KNEX_MODULE],
};
}
}
My application requires access to multiple databases, I know I can do that by creating multiple knex instances. So I tried to register the module twice, passing different configurations. However, the module only registered once. The second register call seems to be reusing the existing object instead of creating a new knex instance.
What is the correct way to generate multiple providers, depending on the configuration passed? The closest thing I found is the forFeature functions in typeORM and Sequelize
I just found the solution. I was thinking the wrong way. I needed to register two providers to my Module. Not create two instances of my module. I solved it by adding one more parameter to my module, which is the provider token. Now it correctly creates the two providers.
import { DynamicModule, Module } from '#nestjs/common';
import { Knex, knex } from 'knex';
import { WINSTON_MODULE_PROVIDER } from 'nest-winston';
import { Logger } from 'winston';
export const KNEX_MODULE = 'KNEX_MODULE';
#Module({})
export class KnexModule {
static register(token: string, options: Knex.Config): DynamicModule {
return {
module: KnexModule,
providers: [
{
inject: [WINSTON_MODULE_PROVIDER],
provide: token,
useFactory: (logger: Logger) => {
logger.info('Creating new knex instance', {
context: KnexModule.name,
tags: ['instance', 'knex', 'create'],
});
return knex(options);
},
},
],
exports: [token],
};
}
}
And whenever I want to use it I register it like this:
#Module({
imports: [KnexModule.register(CatRepository.KNEX_TOKEN, knexConfigs)],
providers: [CatRepository, CatService],
controllers: [CatController],
exports: [CatService],
})
export class CatModule {}
Then in the repository I can inject the knex instance of the cats database.
#Injectable()
export class CatRepository implements Repository<Cat> {
// eslint-disable-next-line no-useless-constructor
public static KNEX_TOKEN = 'KNEX_CATS_TOKEN';
// eslint-disable-next-line no-useless-constructor
constructor(
#Inject(CatRepository.KNEX_TOKEN)
protected knex: Knex,
) {}
...
}

Using nestjs DI with class validator

Seems like a real pain in the brain...
There is a huge thread about this on github and other sites, many of them come down to using useContainer from the 'class-validator' but it does not work for me.
async function bootstrap() {
const app = await NestFactory.create(ApplicationModule);
useContainer(app, { fallback: true });
await app.listen(3000);
}
bootstrap();
Here's the injectable:
#ValidatorConstraint({ name: 'uniqueOnDatabase', async: true })
#Injectable()
export class UniqueOnDatabase implements ValidatorConstraintInterface {
constructor(
private readonly userService: UserService,
) {}
public async validate(val: any, args: ValidationArguments): Promise<boolean> {
const user = await this.userService.retrieveOneByEmail(val);
return !user;
}
public defaultMessage(args: ValidationArguments): string {
return `User with such an email address already exists in the DB`;
}
}
All I want to do is use my userService inside that UniqueOnDatabase class.
Here is the module where I am providing the UniqueOnDatabase:
import { Module, CacheModule } from '#nestjs/common';
import { ConfigModule } from '#nestjs/config';
import { CacheConfigService } from 'src/config/cache/config.service';
import { CacheService } from './services/cache.service';
import { CodeGenService } from './services/code-gen.service';
import { UserExistanceValidationPipe } from './pipes/user-existance.validation.pipe';
import { UsersModule } from 'src/users/users.module';
import { UniqueOnDatabase } from './validators/unique-on-database.validator';
#Module({
providers: [
CacheService,
CodeGenService,
UniqueOnDatabase,
],
imports: [
CacheModule.registerAsync({
imports: [ConfigModule],
useClass: CacheConfigService,
}),
UsersModule,
],
exports: [
CacheService,
CodeGenService,
UniqueOnDatabase,
],
})
export class SharedModule {}
Thanks #Albert for answering your question.
Adding #Albert's answer just in case someone misses the comments:
#JayMcDoniel Aaah, seems like I've figured out the solution. I should
have used useContainer(app.select(SharedModule), { fallbackOnErrors:
true }); instead of what I did at first...
Thanks again #Albert

How to make a dynamic module a global module?

In my application, I want to use a single instance of service class throughout my project. The service class will be initialized by a dynamic module.
Things in detail
I have module called LoggerModule which has a static function register. We use this method to initialize in the app module Eg:LoggerModule.register(options). In the register method, we will be returning a dynamic module which will set this options as a custom provider.
Eg:
return {
module: LoggerModule,
providers: [
{
provide: CONFIG_OPTIONS,
useValue: options,
},
LoggerService,
],
exports: [LoggerService],
};
Here we have a LoggerService that injects the CONFIG_OPTIONS, so that we can fetch options using the service class. Now I want to be able to access the service from anywhere in the project by injecting it in my class, but since the module is not global, currently I will have to include LoggerModule.register() in all the modules that I am using. I tried using the #Global() annotation, but it doesn't seem to work.
Can you suggest any methods on how to do this? If yes please share with me an example?
All you should need to do to make a dynamic module global is add the #Global() decorator above the #Module() decorator. Same with any other module you are working with. Here are the docs on it
Edit 11/22/19
Okay, I tried to mimic your setup as close as I could with what was given and what I still had lying around my local machine. I was able to get a global module working with the following setup:
Config Module (what will be the global module)
import { DynamicModule, Module, Provider, Global } from '#nestjs/common';
import { CONFIG_MODULE_OPTIONS } from './config.constants';
import { createConfigProvider } from './config.provider';
import { ConfigService } from './config.service';
import {
ConfigModuleAsyncOptions,
ConfigModuleOptions,
ConfigOptionsFactory,
} from './interfaces/config-options.interface';
#Global()
#Module({})
export class ConfigModule {
static forRoot(options: ConfigModuleOptions): DynamicModule {
return {
module: ConfigModule,
providers: [ConfigService, ...createConfigProvider(options)],
exports: [ConfigService],
};
}
static forRootAsync(options: ConfigModuleAsyncOptions): DynamicModule {
return {
module: ConfigModule,
imports: options.imports || [],
providers: [ConfigService, ...this.createAsyncProviders(options)],
exports: [ConfigService],
};
}
private static createAsyncProviders(
options: ConfigModuleAsyncOptions,
): Provider[] {
if (options.useExisting || options.useFactory) {
return [this.createAsyncOptionsProviders(options)];
}
if (options.useClass) {
return [
this.createAsyncOptionsProviders(options),
{
provide: options.useClass,
useClass: options.useClass,
},
];
}
throw new Error('Invalid ConfigModule configuration.');
}
private static createAsyncOptionsProviders(
options: ConfigModuleAsyncOptions,
): Provider {
if (options.useFactory) {
return {
provide: CONFIG_MODULE_OPTIONS,
useFactory: options.useFactory,
inject: options.inject || [],
};
}
return {
provide: CONFIG_MODULE_OPTIONS,
useFactory: async (optionsFactory: ConfigOptionsFactory) =>
await optionsFactory.createConfigOptions(),
inject: [options.useExisting || options.useClass || ''],
};
}
}
I had this set up for a completely reusable Nest Module but scrapped the idea as there are already a few config modules out there, hence all the boilerplate.
Dyanmic Module (yes I know it's spelled wrong)
import { Module } from '#nestjs/common';
import { DyanmicTestService } from './dyanmic-test.service';
import { DyanmicTestController } from './dyanmic-test.controller';
#Module({
providers: [DyanmicTestService],
controllers: [DyanmicTestController],
})
export class DyanmicTestModule {}
The Dyanmic Service injects the Config Service, but notice we don't import the Config Module here. That's because it is global, and once registered in the App Module, it doesn't need to be imported anywhere else.
App Module
import { Module } from '#nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { DyanmicTestModule } from './dyanmic-test/dyanmic-test.module';
import { ConfigModule } from './config/config.module';
#Module({
imports: [
ConfigModule.forRootAsync({
useFactory: () => ({
fileName: '.env',
useProcess: false,
}),
}),
DyanmicTestModule,
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
All of this code can also be found on my GitHub.

Nest can't resolve dependencies of the ICommandBusAdapter

Trying to use my ICommandBusAdapter.ts in my CreateUserAction.ts, but I get the following error:
[ExceptionHandler] Nest can't resolve dependencies of the ICommandBusAdapter (?). Please make sure that the argument at index [0] is available in the AdapterModule context
I have created a AdapterModule that will share all providers to others modules, but it doesn't seems work.
Any idea ?
AppModule.ts
import { UserModule } from './User/UserModule';
import { AdapterModule } from './Common/AdapterModule';
#Module({
imports: [AdapterModule, UserModule, // ...],
})
export class AppModule {}
AdapterModule.ts
import { CommandBusAdapter } from 'src/Infrastructure/Adapter/Bus/CommandBusAdapter';
const providers = [
{ provide: 'ICommandBusAdapter', useClass: CommandBusAdapter },
// ...
];
#Module({
providers: [...providers],
exports: [...providers],
})
export class AdapterModule {}
UserModule.ts
import { Module } from '#nestjs/common';
import { CreateUserAction } from 'src/Infrastructure/Action/User/CreateUserAction';
#Module({
controllers: [CreateUserAction],
})
export class UserModule {}
CommandBusAdapter.ts
import { CommandBus, ICommand } from '#nestjs/cqrs';
import { ICommandBusAdapter } from 'src/Application/Adapter/Bus/ICommandBusAdapter';
#Injectable()
export class CommandBusAdapter implements ICommandBusAdapter {
constructor(private readonly commandBus: CommandBus) {}
execute = (command: ICommand) => {
return this.commandBus.execute(command);
};
}
CreateUserAction.ts
import { ICommandBusAdapter } from 'src/Application/Adapter/Bus/ICommandBusAdapter';
export class CreateUserAction {
constructor(
#Inject('ICommandBusAdapter')
private readonly commandBus: ICommandBusAdapter,
) {}
// ...
Did you remember to add the CqrsModule to your application?
import { CqrsModule } from '#nestjs/cqrs';
#Module({
imports: [CqrsModule]
....
Without it there won't anything providing the CommandBus which you're trying to inject.
You can see an example here:
https://github.com/kamilmysliwiec/nest-cqrs-example/blob/master/src/heroes/heroes.module.ts

Resources