Using nestjs DI with class validator - nestjs

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

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.

Cannot inject custom provider for dynamic module

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?

Trying to use in Nestjs Custom Repository typeorm

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

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

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