I am trying to add Bunyan logging library in my NestJs application. I am using nestjs bunyan official npm package which is npm i nestjs-bunyan but it's showing error when I am trying to instantiate inside service class. I am using its documentation and taken reference from here https://www.npmjs.com/package/nestjs-bunyan
Below is my code:
app.module.ts
import { Module } from '#nestjs/common';
import { BunyanLoggerModule } from 'nestjs-bunyan';
import { AppController } from './app.controller';
import { AppService } from './app.service';
#Module({
imports: [BunyanLoggerModule.forRoot({
isGlobal: true,
isEnableRequestLogger: true,
bunyan: {
name: 'MyApp',
},
}),],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
app.controller.ts
import { Controller, Get } from '#nestjs/common';
import { BunyanLoggerModule, ReqLogger } from 'nestjs-bunyan';
import { AppService } from './app.service';
#Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
#ReqLogger() private readonly logger: BunyanLoggerModule
#Get()
getHello(): string {
return this.appService.getHello();
}
}
app.service.ts
import { Injectable, Logger } from '#nestjs/common';
import { BunyanLoggerModule } from 'nestjs-bunyan';
#Injectable()
export class AppService {
#Logger() private readonly logger: BunyanLoggerModule //Showing red line below #Logger()
getHello(): string {
return 'Hello World!';
}
}
How can I resolve this?
From the npm page it looks like that BunyanLoggerModule type should just be Bunyan, presumably from the bunyan package. Otherwise, you could call it Logger from #nestjs/common
Related
Follow tutorial at here, I'm just implement simple NestJS app with a module have injected provider below:
# app.module.ts
import { Module } from '#nestjs/common'
import { ConfigModule } from '#nestjs/config'
import { AuthModule } from '#/modules/auth/auth.module'
#Module({
imports: [
ConfigModule.forRoot(),
AuthModule
]
})
export class AppModule {}
# auth.module.ts
import { Module } from '#nestjs/common'
import { AuthController } from '#/modules/auth/auth.controller'
import { AuthService } from '#/modules/auth/auth.service'
#Module({
controllers: [AuthController],
providers: [AuthService]
})
export class AuthModule {}
# auth.controller.ts
import { Controller, Get } from '#nestjs/common'
import { AuthService } from '#/modules/auth/auth.service'
#Controller('auth')
export class AuthController {
constructor (private readonly service: AuthService) {}
#Get('me')
public getSelfInfo (): string {
return this.service.getSelfInfo()
}
}
# auth.service.ts
import { Injectable } from '#nestjs/common'
#Injectable()
export class AuthService {
getSelfInfo (): string {
return 'ok'
}
}
But when call to endpoint, this error thrown:
[Nest] 17196 - 09/23/2022, 2:34:18 PM ERROR [ExceptionsHandler] Cannot read properties of undefined (reading 'getSelfInfo')
TypeError: Cannot read properties of undefined (reading 'getSelfInfo')
at AuthController.getSelfInfo (/dist/modules/auth/auth.controller.js:16:29)
Please tell me which problem at here.
Solved this issue when add code block below to app.controller.ts:
#Inject(AuthService)
private readonly service: AuthService
constructor (service: AuthService) {
this.service = service
}
by example:
[https://stackoverflow.com/questions/72549668/how-to-do-custom-repository-using-typeorm-mongodb-in-nestjs][1]
Created custom repository в yandex-ndd-api-client.module:
import { DataSource, Repository } from 'typeorm';
import { Injectable } from '#nestjs/common';
// import {Team} from '#Domain/Team/Models/team.entity';
import { TestRepositoryTypeorm } from '../entity/testRepositoryTypeorm.entity';
#Injectable()
export class TestRepository extends Repository<TestRepositoryTypeorm> {
constructor(private dataSource: DataSource) {
super(TestRepositoryTypeorm, dataSource.createEntityManager());
}
async findTest(): Promise<any> { //TestRepositoryTypeorm | undefined
const findTests = await this.dataSource
.getRepository(TestRepositoryTypeorm)
.createQueryBuilder('test')
.getMany();
return await findTests;
}
}
Connected in the module::
providers: [YandexDeliveryService, YandexNddApiClientService, ConfigService, SyncService, TestRepository],
Connected it to the service yandex-ndd-api-client.service:
import { TestRepository } from './repository/testRepositoryTypeorm.retository';
#Injectable()
export class YandexNddApiClientService {
constructor(
// private yandexDeliveryApiService: YandexDeliveryApiService,
private httpService: HttpService,
private dataSource: DataSource,
private configService: ConfigService,
#Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger,
// #InjectRepository(TestRepository)
private testRepository: TestRepository,
) {}
Called in service:
//testRepositoryTypeorm
async testRepositoryTypeorm(): Promise<any> {
try {
console.log('testRepositoryTypeorm');
// return 'testRepositoryTypeorm';
return await this.testRepository.findTest();
} catch (e) {
console.log('ERROR testRepositoryTypeorm:', e);
}
}
As a result:
ERROR [ExceptionHandler] Nest can't resolve dependencies of the YandexNddApiClientService (HttpService, DataSource, ConfigService, winston, ?, SchedulerRegistry). Please make sure that the argument TestRepository at index [4] is available in the DetmirApiClientModule context.
Potential solutions:
- If TestRepository is a provider, is it part of the current DetmirApiClientModule?
- If TestRepository is exported from a separate #Module, is that module imported within DetmirApiClientModule?
#Module({
imports: [ /* the Module containing TestRepository */ ]
})
[1]: https://stackoverflow.com/questions/72549668/how-to-do-custom-repository-using-typeorm-mongodb-in-nestjs
DetmirApiClientModule.ts:
import { Module } from '#nestjs/common';
import { DetmirApiClientService } from './detmir-api-client.service';
import { DetmirApiClientController } from './detmir-api-client.controller';
import { SyncService } from 'src/sync.service';
import { YandexNddApiClientService } from 'src/yandex-ndd-api-client/yandex-ndd-api-client.service';
import { HttpModule, HttpService } from '#nestjs/axios';
import { WINSTON_MODULE_PROVIDER } from 'nest-winston';
import { YandexNddApiClientModule } from 'src/yandex-ndd-api-client/yandex-ndd-api-client.module';
#Module({
providers: [DetmirApiClientService, SyncService, YandexNddApiClientService],
controllers: [DetmirApiClientController],
imports: [HttpModule, YandexNddApiClientModule], //TestRepository
})
export class DetmirApiClientModule {}
Most likely your YandexNddApiClientModule does not add the YandexNddApiClientService to the exports array. Add YandexNddApiClientService to the exports if it is not already there and remove YandexNddApiClientService from the providers array of DetmirApiClientModule. The error is being raised because you have YandexNddApiClientService declared in the providers of DetmirApiClientModule so Nest is trying to create the provider in the new module rather than re-use the module from its original context
I am new to NestJs + Typescript, trying to initialize a project, after building it throws the following error:
ERROR [ExceptionHandler] Nest can't resolve dependencies of the ConfigurationService (ConfigService, ?). Please make sure that the argument CachingService at index [1] is available in the ConfigurationService context.
I tried many possible ways to solve this but no luck yet
These are the two modules in question:
import { CacheModule, Module } from '#nestjs/common';
import { CachingService } from './caching.service';
#Module({
imports: [
CacheModule.register({
ttl: 0,
}),
],
providers: [CachingService],
exports: [CachingService],
})
export class CachingModule {}
import { Module } from '#nestjs/common';
import { ConfigModule } from '#nestjs/config';
import { CachingModule } from '../caching/caching.module';
import { ConfigurationService } from './configuration.service';
import { config } from './environmentConfig/default.config';
import deploymentConfig from './deploymentConfig/config';
import { CachingService } from '../caching/caching.service';
#Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
load: [() => config, deploymentConfig],
}),
CachingModule,
],
providers: [ConfigurationService, CachingService],
exports: [ConfigurationService],
})
export class ConfigurationModule {}
This is the service where I am trying to use the cachingModule and the error is being thrown:
import { Injectable, Logger, OnModuleInit } from '#nestjs/common';
import { ConfigService } from '#nestjs/config';
import { CachingService } from '../caching/caching.service';
#Injectable()
export class ConfigurationService implements OnModuleInit {
private readonly logger = new Logger(ConfigurationService.name);
constructor(
private readonly configService: ConfigService,
private cachingService: CachingService,
) {}
How to fix this?
Remove the CachingService as a provider in the ConfigurationModule.
I don't know how to fix this issue. I keept trying but can't get my head arround it.
The log throws me this
Error: Nest can't resolve dependencies of the ERC20Service (?). Please make sure that the argument dependency at index [0] is available in the ERC20Module context.
import { DynamicModule, Module, Provider } from '#nestjs/common';
import { ERC20Service } from './erc20.service';
const Web3 = require("web3");
export interface ERC20ModuleOptions {
global?: boolean;
abi: Object,
wsEndpoint: string,
httpEndpoint: string
}
export const WEB3_HTTP_TOKEN = 'WEB3_HTTP_TOKEN';
export const WEB3_WS_TOKEN = 'WEB3_WS_TOKEN'
export class ERC20Module {
static forRoot(options: ERC20ModuleOptions): DynamicModule {
const httpProvider: Provider = {
provide: WEB3_HTTP_TOKEN,
useValue:new Web3(new Web3.providers.HttpProvider(options.httpEndpoint))
};
const wsProvider: Provider = {
provide: WEB3_WS_TOKEN,
useValue:new Web3(new Web3.providers.WebsocketProvider(options.wsEndpoint))
};
return {
module: ERC20Module,
providers: [httpProvider, wsProvider, ERC20Service],
exports: [ERC20Service],
global: options.global,
};
}
}
import Web3 from "web3"
import { Inject, Injectable } from '#nestjs/common';
import { WEB3_HTTP_TOKEN, WEB3_WS_TOKEN } from './erc20.module';
#Injectable()
export class ERC20Service {
constructor(#Inject(WEB3_HTTP_TOKEN) private http: Web3,#Inject(WEB3_WS_TOKEN) private ws: Web3) {
}
}
import { Module } from '#nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { ConfigModule } from '#nestjs/config';
import { ERC20Module } from 'src/modules/erc20/erc20.module';
import { abi } from 'src/config/abi';
#Module({
imports: [
ConfigModule.forRoot(),
ERC20Module.forRoot({
global: true,
httpEndpoint: `https://${process.env.CHAINSTACK_USER}:${process.env.CHAINSTACK_PASSWORD}#${process.env.CHAINSTACK_HTTP_ENDPOINT}`,
wsEndpoint: `wss://${process.env.CHAINSTACK_USER}:${process.env.CHAINSTACK_PASSWORD}#${process.env.CHAINSTACK_WS_ENDPOINT}`,
abi: abi,
}),
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
I must be blind because as far as I see it, I am doing everything the correct way when in comes to injecting, exporting and importing. Maybe someone on here can guide me along the way to solve this issue. Thank you in advance
Your erc20.service and erc20.module files import from each other, creating a circular dependency. Move the constants to a separate file and you should be good to go
// erc20.constants.ts
export const WEB3_HTTP_TOKEN = 'WEB3_HTTP_TOKEN';
export const WEB3_WS_TOKEN = 'WEB3_WS_TOKEN'
// erc20.module.ts
import { DynamicModule, Module, Provider } from '#nestjs/common';
import { WEB3_HTTP_TOKEN, WEB3_WS_TOKEN } from './erc20.constants';
import { ERC20Service } from './erc20.service';
const Web3 = require("web3");
export interface ERC20ModuleOptions {
global?: boolean;
abi: Object,
wsEndpoint: string,
httpEndpoint: string
}
export class ERC20Module {
static forRoot(options: ERC20ModuleOptions): DynamicModule {
const httpProvider: Provider = {
provide: WEB3_HTTP_TOKEN,
useValue:new Web3(new Web3.providers.HttpProvider(options.httpEndpoint))
};
const wsProvider: Provider = {
provide: WEB3_WS_TOKEN,
useValue:new Web3(new Web3.providers.WebsocketProvider(options.wsEndpoint))
};
return {
module: ERC20Module,
providers: [httpProvider, wsProvider, ERC20Service],
exports: [ERC20Service],
global: options.global,
};
}
}
// erc20.service.ts
import Web3 from "web3"
import { Inject, Injectable } from '#nestjs/common';
import { WEB3_HTTP_TOKEN, WEB3_WS_TOKEN } from './erc20.constants';
#Injectable()
export class ERC20Service {
constructor(#Inject(WEB3_HTTP_TOKEN) private http: Web3,#Inject(WEB3_WS_TOKEN) private ws: Web3) {
}
}
I am using NestJS 5.4.0
I have custom LoggerService, it's working perfectly. But, how can I add this LoggerService to ExceptionFilter.
// logger.service.ts
import {Injectable, LoggerService} from '#nestjs/common';
#Injectable()
export class Logger implements LoggerService {
log(message: string) {
console.log(message);
}
error(message: string, trace: string) {
console.error(message);
}
warn(message: string) {
console.warn(message);
}
}
//logger.module.ts
import { Module } from '#nestjs/common';
import {Logger} from '../services/logger.service';
#Module({
providers: [Logger],
exports: [Logger],
})
export class LoggerModule {}
// user.module.ts
import { Module } from '#nestjs/common';
import {UserService} from '../services/user.service';
import {LoggerModule} from './logger.module';
#Module({
imports: [LoggerModule],
providers: [UserService],
exports: [UserService],
})
export class UserModule {}
It's working perfectly.
import {Logger} from './logger.service';
export class UserService {
constructor(
private logger: Logger
) {}
private test = () => {
this.logger.log("test"); // log success "test" to console
}
}
But how can I add my custom Logger to ExceptionFilter
// forbidden.exception.filter.ts
import {HttpException, HttpStatus, Injectable} from '#nestjs/common';
#Injectable()
export class ForbiddenException extends HttpException {
constructor(message?: string) {
super(message || 'Forbidden', HttpStatus.FORBIDDEN);
// I want to add my custom logger here!
}
}
Thank for reading.
First of all your class ForbiddenException extends HttpException is not
what it calls ExceptionFilter. ExceptionFilter is
exceptions layer which is responsible for processing all unhandled exceptions across an application
docs
You provided exmaple when you are trying to inject it to your custom HttpException. But thats wrong. Your exception don't have to be responsible for logging. Thats what ExceptionFilter should be responsible for.
Anyway, for now (17 oct 2019) there is no example in official docs how to inject providers to ExceptionFilter.
You can pass it to constructor on init, but you should to get Logger instance before with app.get<T>(...) method.
For example I've changed code from exception-filters docs:
// HttpExceptionFilter.ts
import { ExceptionFilter, Catch, ArgumentsHost, HttpException } from '#nestjs/common';
import { Request, Response } from 'express';
import {MyLogger} from '../MyLogger'
#Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
constructor(private readonly logger: MyLogger) {}
catch(exception: HttpException, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const request = ctx.getRequest<Request>();
const status = exception.getStatus();
if (status >= 500) {
this.logger.error({ request, response });
}
response
.status(status)
.json({
statusCode: status,
timestamp: new Date().toISOString(),
path: request.url,
});
}
}
and bootstrap.ts code:
// bootstrap.ts
const app = await NestFactory.create(MainModule, {
logger: false,
});
const logger = app.get<MyLogger>(MyLogger);
app.useLogger(logger);
app.useGlobalFilters(new HttpExceptionFilter(logger));
This technique can be used for all this INestApplication methods:
app.useGlobalFilters
app.useGlobalGuards
app.useGlobalInterceptors
app.useGlobalPipes
app.useLogger
app.useWebSocketAdapter
First of all, to use dependency injection with Exception filters you cannot register them using the useGlobalFilters() method:
const app = await NestFactory.create(MainModule, {
logger: false,
});
const logger = app.get<MyLogger>(MyLogger);
app.useLogger(logger);
//Remove this line
//app.useGlobalFilters(new HttpExceptionFilter(logger));
Next in your MainModule, add your custom exception filter as a provider (note: filters are automatically set as global no matter what module you add them to but as a best practice, add them to your top level module):
import { Module } from '#nestjs/common';
import { APP_FILTER } from '#nestjs/core';
import { LoggerModule } from './logger.module';
import { ForbiddenException } from './forbidden.exception.filter.ts';
#Module({
imports: [
LoggerModule //this is your logger module
],
providers: [
{
provide: APP_FILTER, //you have to use this custom provider
useClass: ForbiddenException //this is your custom exception filter
}
]
})
export class MainModule {}
Now you can inject the logger into your custom exception filter:
import {HttpException, HttpStatus, Injectable} from '#nestjs/common';
import { Logger } from './path/to/logger';
#Injectable()
export class ForbiddenException extends HttpException {
constructor(private logger: Logger) {}
catch(exception: HttpException, response) {
this.logger.log('test');
}
}
Pseudo code but I think you get the idea.