I have created a module called DmaModule
, and expect the consumer of it to pass in some options into it during its instantiation. These options are dmaHost
, superadminUsername
, and superadminPassword
. In order for me to get these options, I have to grab them from the environment variables.
I want to pass them as options, and I don't want to read them as environment variables inside the module.
Here is my AppModule
:
import { Module } from '@nestjs/common';
import { DmaModule } from '@app/dma';
import { ConfigModule, ConfigService } from '@nestjs/config';
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true, // Makes the ConfigModule globally available
envFilePath: `.env.${process.env.NODE_ENV}`,
}),
DmaModule.forRootAsync({
inject: [ConfigService],
imports: [ConfigModule],
useFactory: (config: ConfigService) => ({
dmaHost: config.get<string>('DMA_HOST'),
superadminUsername: config.get<string>('DMA_SUPERADMIN_USERNAME'),
superadminPassword: config.get<string>('DMA_SUPERADMIN_PASSWORD'),
}),
}),
],
controllers: [],
providers: [],
})
export class AppModule {}
And here is my DmaModule
:
import { DynamicModule, Module } from '@nestjs/common';
import { DmaClientProvider } from './providers/dma-client-provider/dma-client-provider';
export const DMA_PROPS_TOKEN = 'DMA_MODULE_OPTIONS';
export interface DmaModuleProps {
dmaHost: string;
superadminUsername: string;
superadminPassword: string;
}
interface RootOptions {
imports: any[];
inject: any[];
useFactory: (...args: any[]) => DmaModuleProps;
}
@Module({
imports: [],
providers: [DmaClientProvider], // << š take a look here
exports: [],
})
export class DmaModule {
static forRootAsync(options: RootOptions): DynamicModule {
return {
module: DmaModule,
providers: [
{
provide: DMA_PROPS_TOKEN,
useFactory: options.useFactory,
inject: options.inject,
},
],
exports: [DMA_PROPS_TOKEN],
};
}
}
I want now to start using these options passed to the DmaModule
and be able to give them to the services and providers listed under the DmaModule
.
So I have a provider called DmaClientProvider
, and I want to simply console.log()
the options passed from the AppModule
down to the DmaModule
inside of the DmaClientProvider
.
Here is my DmaClientProvider
code:
import { Inject, Injectable } from '@nestjs/common';
import { DMA_PROPS_TOKEN, DmaModuleProps } from '@app/dma/dma.module';
@Injectable()
export class DmaClientProvider {
constructor(
@Inject(DMA_PROPS_TOKEN) private readonly options: DmaModuleProps,
) {
console.log(options);
}
}
I thought it will work, but I am getting this error:
ā°ā>$ npm run start:dev 20:02:11
> api.stb-radiusmanager_nestjs@0.0.1 start:dev
> cross-env NODE_ENV=development nest start --watch
Info Webpack is building your sources...
webpack 5.92.1 compiled successfully in 408 ms
Type-checking in progress...
[Nest] 290429 - 07/12/2024, 8:02:17 PM LOG [NestFactory] Starting Nest application...
[Nest] 290429 - 07/12/2024, 8:02:17 PM LOG [InjectorLogger] Nest encountered an undefined dependency. This may be due to a circular import or a missing dependency declaration.
[Nest] 290429 - 07/12/2024, 8:02:17 PM ERROR [ExceptionHandler] Nest can't resolve dependencies of the DmaClientProvider (?). Please make sure that the argument dependency at index [0] is available in the DmaModule context.
Potential solutions:
- Is DmaModule a valid NestJS module?
- If dependency is a provider, is it part of the current DmaModule?
- If dependency is exported from a separate @Module, is that module imported within DmaModule?
@Module({
imports: [ /* the Module containing dependency */ ]
})
Error: Nest can't resolve dependencies of the DmaClientProvider (?). Please make sure that the argument dependency at index [0] is available in the DmaModule context.
Potential solutions:
- Is DmaModule a valid NestJS module?
- If dependency is a provider, is it part of the current DmaModule?
- If dependency is exported from a separate @Module, is that module imported within DmaModule?
@Module({
imports: [ /* the Module containing dependency */ ]
})
Here is my folder structure:
ā°ā>$ tree -I node_modules/ 21:16:25
.
āāā dist
āĀ Ā āāā main.js
āāā libs
āĀ Ā āāā dma
āĀ Ā āāā src
āĀ Ā āĀ Ā āāā dma.module.ts
āĀ Ā āĀ Ā āāā dma.service.spec.ts
āĀ Ā āĀ Ā āāā dma.service.ts
āĀ Ā āĀ Ā āāā index.ts
āĀ Ā āĀ Ā āāā modules
āĀ Ā āĀ Ā āĀ Ā āāā dma-user-groups
āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā dma-user-groups.module.ts
āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā dma-user-groups.service.spec.ts
āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā dma-user-groups.service.ts
āĀ Ā āĀ Ā āĀ Ā āāā dma-users
āĀ Ā āĀ Ā āĀ Ā āāā dma-users.module.ts
āĀ Ā āĀ Ā āĀ Ā āāā dma-users.service.spec.ts
āĀ Ā āĀ Ā āĀ Ā āāā dma-users.service.ts
āĀ Ā āĀ Ā āāā providers
āĀ Ā āĀ Ā āāā dma-client-provider
āĀ Ā āĀ Ā āāā dma-client-provider.spec.ts
āĀ Ā āĀ Ā āāā dma-client-provider.ts
āĀ Ā āĀ Ā āāā dmaEndpoints.ts
āĀ Ā āāā tsconfig.lib.json
āāā nest-cli.json
āāā package.json
āāā package-lock.json
āāā README.md
āāā src
āĀ Ā āāā app.controller.spec.ts
āĀ Ā āāā app.controller.ts
āĀ Ā āāā app.module.ts
āĀ Ā āāā app.service.ts
āĀ Ā āāā main.ts
āāā test
āĀ Ā āāā app.e2e-spec.ts
āĀ Ā āāā jest-e2e.json
āāā tsconfig.build.json
āāā tsconfig.json
11 directories, 28 files
The solution was to simply use the name of the token litrally, and not to import it, I don't know why, if someone knows please let me know in the comments so I can update my answer.
I needed to change the
import { DMA_PROPS_TOKEN, DmaModuleProps } from '@app/dma/dma.module
// ...
@Inject(DMA_PROPS_TOKEN) private readonly options: DmaModuleProps
to
import { DMA_PROPS_TOKEN, DmaModuleProps } from '@app/dma/dma.module
// ...
@Inject("DMA_PROPS_TOKEN") private readonly options: DmaModuleProps