NestJS problems with shared modules - node.js

I am trying to create a shared logging module in NestJS that can be shared between multiple micro-services.
The logging module works when it is part of the micro-service but when I extract the code to its own NPM module it no longer works.
Below is a sample of my shared NPM module code:
// my-logger.module.ts
import { Module } from '#nestjs/common';
import { ConfigModule, ConfigService } from '#nestjs/config';
import { LoggerModule } from 'nestjs-pino';
#Module({
imports: [
LoggerModule.forRootAsync({
imports: [ConfigModule],
useFactory: async (configService: ConfigService) => ({
pinoHttp: {
level: process.env.LOG_LEVEL || 'info',
redact: configService.get<string[]>('logger.redacted.fields'),
prettyPrint: {
colorize: false,
singleLine: true,
levelFirst: false,
translateTime: "yyyy-mm-dd'T'HH:MM:ss.l'Z'",
messageFormat: '{req.headers.x-correlation-id} [{context}] {msg}',
ignore: 'pid,hostname,context,req,res,responseTime',
errorLikeObjectKeys: ['err', 'error'],
},
},
}),
inject: [ConfigService],
}),
],
controllers: [],
providers: [],
})
export class MyLoggerModule {}
Below is a sample of the App Module from my NestJS micro-service
// app.module.ts
import { Module } from '#nestjs/common';
import { ConfigModule } from '#nestjs/config';
import configuration from '../config/configuration';
import { MyLoggerModule } from '#my-company/my-logger.module';
import { HttpModule } from '#nestjs/axios';
#Module({
imports: [
ConfigModule.forRoot({ load: [configuration] }),
MyLoggerModule,
HttpModule,
],
controllers: [],
providers: [],
})
export class AppModule {}
The micro-service builds and deploys correctly with the shared npm module. However, each time I send a new request it causes the service to restart with the following error:
node[1]: ../src/tcp_wrap.cc:149:static void node::TCPWrap::New(const v8::FunctionCallbackInfo<v8::Value>&): Assertion `args[0]->IsInt32()' failed.
Does anyone have any ideas on why this is failing ?
Note: I am guessing it has something to do with the ConfigService/ConfigModule being used in both modules. However, I don't understand why the same code works when it is part of the micro-service

We experienced similar issues trying to configure shared modules and ran into a whole host of issues. The root cause was the package versions and that these were not aligned. This answer pointed me in the right direction.
In summary, double check all package version numbers across both applications and shared packages.

Related

nestjs providing DataSource dependency not working whit async initialization

I need to use the ConfigService to get the environment variables, but when I use "forRootAsync" of the TypeOrmModule nestjs is unable to resolve the DataService dependency
This is my module
#Module({
imports: [
ConfigModule.forRoot({ validate: validateConfig(AssetEnvironmentVariables), isGlobal: true }),
TypeOrmModule.forRootAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: (configService: ConfigService): TypeOrmModuleOptions => ({
type: "postgres",
host: configService.get("PG_HOST"),
port: configService.get("PG_PORT"),
database: configService.get("PG_DB_NAME"),
username: configService.get("PG_USER"),
password: configService.get("PG_PASSWORD"),
entities: [AssetEntity],
migrations: ["./migrations/*"],
}),
});
],
providers: [
{ provide: AssetRepository, useClass: AssetPostgresRepository },
],
})
export class AssetModule {}
This is the implementation to AssetRepository
#Injectable()
export class AssetPostgresRepository extends AssetRepository {
private typeOrmRepository: Repository<AssetEntity>;
constructor(dataSource: DataSource) {
super();
this.typeOrmRepository = dataSource.getRepository(AssetEntity);
}
async save(asset: Asset): Promise<void> {
try {
await this.typeOrmRepository.save(asset.toPrimitives());
} catch (e) {
throw new SavingRepositoryException(e);
}
}
}
This is the error that it throw me
ERROR [ExceptionHandler] Nest can't resolve dependencies of the AssetPostgresRepository (?). Please make sure that the argument DataSource at index [0] is available in the AssetModule context.
Potential solutions:
- Is AssetModule a valid NestJS module?
- If DataSource is a provider, is it part of the current AssetModule?
- If DataSource is exported from a separate #Module, is that module imported within AssetModule?
#Module({
imports: [ /* the Module containing DataSource */ ]
})

Nest can't resolve dependencies of the DiscoveryService (?). Please make sure that the argument ModulesContainer at index [0] is available in

In a NestJs project, I created a module that uses, DiscoveryService in the services. It working fine. I wanted to create a nest library from that module. I followed this nestJs documentation then I am getting the below error. I tried both adding ModulesContainer in to my module imports and providers. nothing helped.
[Nest] 65775 - 10/12/2022, 11:55:03 AM ERROR [ExceptionHandler] Nest can't resolve dependencies of the DiscoveryService (?). Please make sure that the argument ModulesContainer at index [0] is available in the DiscoveryModule context.
Error log
Potential solutions:
If ModulesContainer is a provider, is it part of the current DiscoveryModule?
If ModulesContainer is exported from a separate #Module, is that module imported within DiscoveryModule? #Module({
imports: [ /* the Module containing ModulesContainer */ ] })
Error: Nest can't resolve dependencies of the DiscoveryService (?).
Please make sure that the argument ModulesContainer at index [0] is
available in the DiscoveryModule context.
library
import { DiscoveryService } from '#nestjs/core';
#Injectable()
export class EventPublishingService {
private readonly logger = new Logger(EventPublishingService.name);
private readonly publishingEventProviders;
constructor(
private discoveryService: DiscoveryService
) {
this.publishingEventProviders = this.discoveryService
.getProviders()
.filter(
({ metatype }) =>
metatype && Reflect.getMetadata(PUBLISHABLE_METADATA, metatype),
);
}
}
#Global()
#Module({
imports: [DiscoveryModule, HttpModule, ConfigModule],
providers: [
EventRegistrationService,
EventManagerServiceApi,
EventPublishingService],
exports: [EventPublishingService],
})
export class EventManagerModule {}
app module
#Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
cache: true,
load: [configuration],
}),
ScheduleModule.forRoot(),
MongooseModule.forRootAsync({
imports: [DataVaultManagerModule],
inject: [DataVaultManager],
useClass: MongooseConfigService,
}),
ParticipantsModule,
StudiesModule,
HealthModule,
EventManagerModule,
UsersModule,
],
controllers: [AppController],
providers: [
AppService,
{
provide: APP_PIPE,
useClass: ZodValidationPipe,
},
],
})
export class AppModule {}
Try changing constructor from EventPublishingService class like below:
constructor(
#Inject(DiscoveryService) private discoveryService: DiscoveryService
)

Dynamic ConfigModule cannot Inject in custom JwtModule

I created my Dynamic configModule to extract environment variables from a different path. It extracts from an yml file. Everything works properly if a add in some module. Here is my ConfigModule:
import { DynamicModule } from '#nestjs/common';
import { ConfigModule } from '#nestjs/config';
import { EnvConfigService } from './env.config.service';
export class EnvConfigModule {
/**
* Create an static function to call directly from the class without instantiation
* #param options: Our config module attributes or properties
* #returns DynamicModule
*/
static register(options): DynamicModule {
return {
module: ConfigModule,
providers: [
{
provide: 'CONFIG_OPTIONS',
useValue: options,
},
EnvConfigService,
],
exports: [EnvConfigService],
};
}
}
Now when I want to add that configuration in the new custom JwtModule, CustomJwtModule:
...
import { EnvConfigModule } from 'src/utils/environment/env.config.module';
import { EnvConfigService } from 'src/utils/environment/env.config.service';
#Module({
imports: [
JwtModule.registerAsync({
imports: [EnvConfigModule],
inject: [EnvConfigService],
useFactory: (configService: EnvConfigService) => {
const base64_pubKey = configService.get(ACCESS_PUBLIC_KEY_NAME);
return {
publicKey: Buffer.from(base64_pubKey, KEY_ENCODING),
signOptions: {
algorithm: ENCRYPTION_ALGORITHM,
},
};
},
}),
],
providers: [
{
provide: JWT_ACCESS_SERVICE,
useExisting: JwtService,
},
],
exports: [JWT_ACCESS_SERVICE],
})
export class JWTAccessModule {}
And here is my error:
Error: Nest can't resolve dependencies of the JWT_MODULE_OPTIONS (?). Please make sure that the argument EnvConfigService at index [0] is available in the JwtModule context.
I guess, I do get that error because it does not inject properly in JWT module my service.
My app.module.ts is implemented in that way
...
import { EnvConfigModule } from './utils/environment/env.config.module';
#Module({
imports: [
IamModule,
EnvConfigModule.register({
folder: 'config/environment/',
}),
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
Ok, it seems I was importing wrongly. It was missing some part of the configuration. Here is the working example of CustomJwtModule:
...
import { EnvConfigModule } from 'src/utils/environment/env.config.module';
import { EnvConfigService } from 'src/utils/environment/env.config.service';
#Module({
imports: [
JwtModule.registerAsync({
imports: [EnvConfigModule.register({
folder: 'config/environment/',
}),
],
inject: [EnvConfigService],
...

Proper way to export dynamic module

I have a NestJS application with the CoreModule which exports an collection of the core services to all other modules.
In core module i have dynamic module register HttpModule.
I won't to register this module twice for import and export so i do the following:
const httpModule = HttpModule.register({
timeout: Configuration.requestTimeoutMilliseconds
});
#Module({
imports: [httpModule],
providers: [
...
],
exports: [
httpModule,
...
]
})
export class CoreModule { }
When i start my app i see in InstanceLoader logs HttpModule dependencies registered twice:
What the proper way to export dynamic modules in general?
You can do it like this.
#Module({
imports: [
HttpModule.register({
timeout: Configuration.requestTimeoutMilliseconds,
}),
],
providers: [],
exports: [HttpModule],
})
export class CoreModule {}
Makes sense to me because when you want to implement a dynamic module, you need to return module class as a module identifier in the module field.
There are examples of declaring dynamic modules on this page in documentation https://docs.nestjs.com/fundamentals/dynamic-modules#module-configuration

NestJS - Can't resolve queue?

I am following doc to start to use queue. I installed #nestjs/bull, bull, #types/bull dependencies. And here is my app.module.ts:
#Module({
imports: [
ConfigModule.forRoot({
load: [configuration],
}),
BullModule.registerQueue({
name: 'create_checkin',
redis: {
host: 'localhost',
port: 6379,
},
}),
EventModule,
],
})
export class AppModule {}
I imported BullModule in root module. And here is my event.service.ts:
#Injectable()
export class EventService {
constructor(
#InjectQueue('create_checkin') private readonly createCheckinQueue: Queue,
) {
}
}
And when I start server, I got the following error message:
Nest can't resolve dependencies of the EventService
Please make sure that the argument BullQueue_create_checkin at index [0] is available in the EventModule context.
I don't know which step I did wrong. Someone can help me?
Had a similar problem, in my case it was enough to add the BullModule to the exports array in order to successfully run the whole project. Like this:
#Module({
imports: [
BullModule.registerQueue({ ... }),
...
],
...
exports: [
BullModule, // <— this is important!
...
]
})
Then in my service I've been able to inject the queue:
#InjectQueue('print') private queue: Queue<PrintJob>
You have to import bull module inside the module you are trying to setup queue. You can also refer https://medium.com/#shikhar01.cse14/bull-queues-in-nestjs-306c51cb0ec2
Make sure you are placing EventService under providers array in EventModule.
#Module({
providers: [EventService],
controllers :[],
imports: [YOUR_MODULES],
exports: [EventService]
})
export class EventModule {}
Try importing BullModule straight in Event Module - I had the same problem and doing it this way make it work.
#Module({
imports: [
BullModule.registerQueueAsync({
name: 'csv',
useFactory: async (config: ConfigService) => ({
redis: config.get('redis'),
}),
inject: [ConfigService],
}),
],
providers: [
CsvService
],
exports: [CsvService],
})
export class CsvModule {}
I know that it's async method, but maybe you should try.
You can do like below
#Module({
imports: [
ConfigModule.forRoot({
load: [configuration],
}),
EventModule,
],
})
export class AppModule {}
#Module({
imports: [
BullModule.registerQueue({
name: 'create_checkin',
redis: {
host: 'localhost',
port: 6379,
},
}),
],
})
export class EventModule {}

Resources