NestJS serving JSON and adds a "default" section repeating the JSON - nestjs

I have a strange behaviour on an endpoint in NestJS serving a piece of JSON.
The JS with the JSON object is exporting
module.exports = Object.freeze({
translation: {
TestMessage: 'Bienvenue à React et react-i18next'
}
});
The result on the Client is:
{
"translation": {
"TestMessage": "Bienvenue à React et react-i18next"
},
"default": {
"translation": {
"TestMessage": "Bienvenue à React et react-i18next"
}
}
}
The question is where is the "default" coming from?
To paint the whole picture, below the module, controller and service:
Module
import { Module } from '#nestjs/common';
import { LoggerService } from '#modules/logger';
import { I18nController } from './i18n.controller';
import { I18nService } from './i18n.service';
#Module({
controllers: [I18nController],
providers: [I18nService, LoggerService],
exports: [I18nService]
})
export class I18nModule {}
Controller
import { Controller, Get, Param } from '#nestjs/common';
import { LoggerService } from '#modules/logger';
import { I18nService } from './i18n.service';
#Controller('i18n')
export class I18nController {
constructor(private logger: LoggerService, private i18nService: I18nService) {
this.logger.setContext(I18nController.name);
}
#Get('/:lang')
async getLanguage(#Param('lang') lang: string) {
console.log(lang);
return await this.i18nService.findOneByLanguageCode(lang);
}
}
Service
import { Injectable } from '#nestjs/common';
import { access } from 'fs/promises';
import { constants as fsconstants } from 'fs';
#Injectable()
export class I18nService {
async findOneByLanguageCode(language: string): Promise<any | null> {
const languagefile = __dirname + '/../../public/languages/' + language + '.js';
await access(languagefile, fsconstants.R_OK);
return await import(languagefile);
}
}
From the Client I do a simple http://localhost:3001/i18n/fr-FR
and get the above result.
Again, where is the 'default' section coming from?

There should be esModuleInterop enabled in your tsconfig.json
https://www.typescriptlang.org/tsconfig#esModuleInterop
a default import like import moment from "moment" acts the same as const moment = require("moment").default
That's why you have default object exist.
tsconfig.json
{
"compilerOptions": {
"esModuleInterop": true, // change it to false or remove it
}
}

Related

NestJS / TypeORM: Custom repository method is not accessible in service

New to NestJS and TypeORM, and the similar questions on SO didn't solve my problem.
I have a custom TypeORM repository in NestJS using it in service, but it fails with error:
TypeError: this.tenantRepository.createTenant is not a function.
tenants.module.ts:
import { TenantRepository } from './tenant.repository';
#Module({
imports: [
TypeOrmModule.forFeature([TenantRepository]),
],
controllers: [TenantsController],
providers: [TenantsService],
})
export class TenantsModule { }
tenant.repository.ts:
// ...
import { TenantEntity } from './entities/tenant.entity';
#EntityRepository(TenantEntity)
export class TenantRepository extends Repository<TenantEntity>{
async createTenant(createTenantDto: CreateTenantDto): Promise<TenantEntity> {
const { name, email } = createTenantDto;
const newTenant = new TenantEntity()
newTenant.name = name;
newTenant.email = email;
await newTenant.save()
return newTenant;
}
}
And here's where the error is triggered (tenants.service.ts)
// ...
import { TenantEntity } from './entities/tenant.entity';
import { TenantRepository } from './tenant.repository';
#Injectable()
export class TenantsService {
constructor(
#InjectRepository(TenantRepository)
private tenantRepository: TenantRepository
) { }
async createTenant(createTenantDto: CreateTenantDto): Promise<TenantEntity> {
return await this.tenantRepository.createTenant(createTenantDto); // <-- ERROR
}
}
I can inject entity in service and use it for simple CRUD, but I want to separate concerns and use the repository pattern.
This is a POST endpoint and the error is only after submission from Swagger.
Also, VS Code autocomplete is suggesting createTenant after typing this.tenantRepository
Where am I going wrong?
EntityRepository decorator was deprecated, and as far as I know, you need to define a custom class that extends Repository and decorate it with #Injectable. Hence, you need to have some changes as follows:
tenant.repository.ts:
import { Injectable } from '#nestjs/common';
import { DataSource, Repository } from 'typeorm';
#Injectable()
export class TenantRepository extends Repository<TenantEntity>{
constructor(private dataSource: DataSource) {
super(TenantEntity, dataSource.createEntityManager());
}
async createTenant(createTenantDto: CreateTenantDto): Promise<TenantEntity> {
const { name, email } = createTenantDto;
const newTenant = this.create({ name, email });
await this.save(newTenant);
return newTenant;
}
}
tenants.module.ts:
import { TenantRepository } from './tenant.repository';
#Module({
imports: [
TypeOrmModule.forFeature([TenantRepository]),
],
controllers: [TenantsController],
providers: [TenantsService, TenantRepository],
})
export class TenantsModule { }
tenants.service.ts:
import { TenantEntity } from './entities/tenant.entity';
import { TenantRepository } from './tenant.repository';
#Injectable()
export class TenantsService {
constructor(
private tenantRepository: TenantRepository
) { }
async createTenant(createTenantDto: CreateTenantDto): Promise<TenantEntity> {
return await this.tenantRepository.createTenant(createTenantDto);
}
}
You also have access to built-in typeorm methods like save, create, find, etc. since the custom repository is derived from Repository class.

Nestjs - Can't resolve dependencies of service

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

Error injecting dependency into controller with tsyringe

I am using express and tsyringe for dependency injections.
When I try to use the service dependency on my controller I get an error UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'addressService' of undefined in file addressController.ts
I've searched for several videos and articles and I couldn't find a way to solve them. I will leave my code below
addressRepository.ts
import { AddressResponseDTO } from '../../dtos/response/address/addressResponseDTO';
export interface IAddressRepository {
getAddresses(): Array<AddressResponseDTO>;
}
addressRepositoryImpl.ts
import { AddressResponseDTO } from '../../dtos/response/address/addressResponseDTO';
import { IAddressRepository } from '../declarations/addressRepository';
export default class AddressRepositoryImpl implements IAddressRepository {
getAddresses(): Array<AddressResponseDTO> {
const address1: AddressResponseDTO = {
id: 'ADR123',
postalCode: '17014273',
address: 'Rua José Aiello',
district: 'Centro',
number: '347',
city: 'Bauru',
state: 'São Paulo',
phoneNumber: '16997102842',
clientName: 'João Mário Marcelo Campos',
};
const address2: AddressResponseDTO = {
id: 'ADR456',
postalCode: '07135290',
address: 'Rua Mariano Moya Peramos',
district: 'Jardim Adriana',
number: '1103',
city: 'Guarulhos',
state: 'São Paulo',
phoneNumber: '16997102842',
clientName: 'João Mário Marcelo Campos',
};
return [address1, address2];
}
}
addressService.ts
import { AddressResponseDTO } from '../../dtos/response/address/addressResponseDTO';
import ResultResponseDTO from '../../dtos/response/resultResponseDTO';
export interface IAddressService {
getAddresses(): ResultResponseDTO<Array<AddressResponseDTO>>;
}
addressServiceImpl.ts
import { StatusCodes } from 'http-status-codes';
import { inject, injectable } from 'tsyringe';
import logger from '../../configurations/logger';
import { AddressResponseDTO } from '../../dtos/response/address/addressResponseDTO';
import ResultResponseDTO from '../../dtos/response/resultResponseDTO';
import { IAddressRepository } from '../../repositories/declarations/addressRepository';
import { IAddressService } from '../declarations/addressService';
#injectable()
export default class AddressServiceImpl implements IAddressService {
private addressRepository: IAddressRepository;
constructor(#inject('AddressRepository') addressRepository: IAddressRepository) {
this.addressRepository = addressRepository;
}
getAddresses(): ResultResponseDTO<AddressResponseDTO[]> {
try {
logger.info('AddressService.getAddresses - start');
const addresses = this.addressRepository.getAddresses();
logger.info('AddressService.getAddresses - end');
return ResultResponseDTO.ok(StatusCodes.OK, '', addresses);
} catch (error) {
logger.error(`AddressService.getAddresses - error - message ${error}`);
return ResultResponseDTO.fail(
StatusCodes.INTERNAL_SERVER_ERROR,
'Error getting addresses',
error,
);
}
}
}
addressController.ts
Error at line: const result = this.addressService.getAddresses();
import { Request, Response } from 'express';
import { inject, injectable } from 'tsyringe';
import { IAddressService } from '../services/declarations/addressService';
#injectable()
export class AddressController {
constructor(#inject('AddressService') private addressService: IAddressService) {}
public async getAddresses(request: Request, response: Response): Promise<Response> {
const result = this.addressService.getAddresses();
return response.status(result.httpStatus).json(result.getResponse());
}
}
routes.ts
import { Router } from 'express';
import { container } from 'tsyringe';
import { AddressController } from './controllers/addressController';
import AddressRepositoryImpl from './repositories/implementations/addressRepositoryImpl';
import AddressServiceImpl from './services/implementations/addressServiceImpl';
const routes = Router();
container
.register('AddressRepository', {
useClass: AddressRepositoryImpl,
})
.resolve(AddressRepositoryImpl);
container
.register('AddressService', {
useClass: AddressServiceImpl,
})
.resolve(AddressServiceImpl);
const addressController = container.resolve(AddressController);
routes.get('/', (request, response) => {
response.send('It works!');
});
// AddressController
routes.route('/v1/address').get(addressController.getAddresses);
export default routes;
You register service inside IoC as 'AddressServiceImpl' and after that try to call to it as 'AddressService'.
// service file
#injectable()
export default class AddressServiceImpl implements IAddressService {
...
// controller file
#injectable()
export class AddressController {
constructor(#inject('AddressService') private addressService: IAddressService) {}

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

NestJS How to add custom Logger to custom ExceptionFilter

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.

Resources