How we can access to this of controller nestjs - nestjs

I have property which is configService and I want to access to this property inside an object. How can I do that?
This is the property in the constructor:
#Controller('user')
export class UserController {
constructor(
private userService:UserService,
private configService:ConfigService
){
}
#UseGuards(JwtAuthGuard)
#Get()
say(#CurrentUser() user){
return 'hello user you are '+user.profil+' and your id info is '+user.userProfil.id +' '+this.configService.get('DEST_FORMATIONS')
}
here it works and it print the variable DEST_FORMATIONS of file .env
but here it doesn't work
#UseInterceptors(FileInterceptor('file', {
storage:diskStorage({destination: this.configService.get('DEST_FORMATIONS'), ...
How can I do it?

If you want to use configuration outside Nest's IoC container, you can try nest-typed-config:
import { selectConfig } from 'nest-typed-config';
import { ConfigModule, FileConfig } from '#/module/config';
const fileConfig = selectConfig(ConfigModule, FileConfig);
#UseInterceptors(FileInterceptor('file', {
storage: diskStorage({
destination: fileConfig.destination
})
}))

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 v9: implement durable providers

[SOLVED] I'm pretty new to NestJS and trying to get my head around durable providers but i can't get them to work.
My scenario is that i have a service with some logic and two providers that implement the same interface to get some data. Depending on a custom header value i want to use Provider1 or Provider2 and the service itself does not have to know about the existing provider implementations.
Since i'm in a request scoped scenario but i know there are only 2 possible dependency-subtrees i want to use durable providers that the dependencies are not newly initialised for each request but reused instead.
I set up the ContextIdStrategy as described in the official docs and it is executed on each request but i miss the part how to connect my provider implementations with the ContextSubtreeIds created in the ContextIdStrategy.
Interface:
export abstract class ITest {
abstract getData(): string;
}
Implementations:
export class Test1Provider implements ITest {
getData() {
return "TEST1";
}
}
export class Test2Provider implements ITest {
getData() {
return "TEST2";
}
}
Service:
#Injectable()
export class AppService {
constructor(private readonly testProvider: ITest) {}
getHello(): string {
return this.testProvider.getData();
}
}
Controller:
#Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
#Get()
getData(): string {
return this.appService.getData();
}
}
ContextIdStrategy:
const providers = new Map<string, ContextId>([
["provider1", ContextIdFactory.create()],
["provider2", ContextIdFactory.create()],
]);
export class AggregateByProviderContextIdStrategy implements ContextIdStrategy {
attach(contextId: ContextId, request: Request) {
const providerId = request.headers["x-provider-id"] as string;
let providerSubTreeId: ContextId;
if (providerId == "provider1") {
providerSubTreeId = providers["provider1"];
} else if (providerId == "provider2") {
providerSubTreeId = providers["provider2"];
} else {
throw Error(`x-provider-id ${providerId} not supported`);
}
// If tree is not durable, return the original "contextId" object
return (info: HostComponentInfo) =>
info.isTreeDurable ? providerSubTreeId : contextId;
}
}
Main:
async function bootstrap() {
const app = await NestFactory.create(AppModule);
ContextIdFactory.apply(new AggregateByProviderContextIdStrategy());
await app.listen(3000);
}
bootstrap();
Module:
#Module({
imports: [],
controllers: [AppController],
providers: [
{
provide: ITest,
useFactory: () => {
// THIS IS THE MISSING PIECE.
// Return either Test1Provider or Test2Provider based on the ContextSubtreeId
// which is created by the ContextIdStrategy
return new Test1Provider();
},
},
AppService,
],
})
export class AppModule {}
The missing part was a modification of the ContextIdStrategy return statement:
return {
resolve: (info: HostComponentInfo) => {
const context = info.isTreeDurable ? providerSubTreeId : contextId;
return context;
},
payload: { providerId },
}
after that change, the request object can be injected in the module and where it will only contain the providerId property and based on that, the useFactory statement can return different implementations

How to get name of module, which controller processing request?

I want to get name of module, which controller processing request.
#Get('/')
getIndex() {
console.log('name of module');
}
I don't know exactly the purpose behind your question, but I'll leave you some alternatives.
First one and the dirtier. You can get the instance of the module that your productController is imported by finding it into the modules container.
import { Controller, Get, Query } from '#nestjs/common';
import { ModulesContainer } from '#nestjs/core';
import { Module } from '#nestjs/core/injector/module';
#Controller('path')
export class ProductController {
constructor(
private modulesContainer: ModulesContainer,
private productService: ProductService
) { }
#Get()
findAll(#Query() dto: any) {
let yourModule: Module;
this.modulesContainer.forEach((v) => {
if(v.controllers.has(this.constructor.name)) { // in this condition, you will find your module based in the imports from it, if your controller is importe in some module it will get the module and put in "yourModule" variable.
// Here
yourModule= v;
}
});
console.log(yourModule);
return this.productService.findAll();
}
}
And for a cleaner approach you can get the moduleRef in your controller
import { Controller, Get, Query } from '#nestjs/common';
import { ModuleRef} from '#nestjs/core';
#Controller('path')
export class ProductController {
constructor(
private moduleRef: ModuleRef,
private productService: ProductService
) { }
#Get()
findAll(#Query() dto: any) {
console.log(this.moduleRef) //your module ref
return this.productService.findAll();
}
}
But of course depends on what's you're trying to do.

How to add file and other inputs in a POST api into swagger properly?

I am trying to create swagger documentation for a file upload API with NestJS.
This is what I currently have in my controller:
#ApiTags('files')
#Controller('files')
export class FilesController {
#ApiConsumes('multipart/form-data')
#ApiCreatedResponse({description: "This api creates a file in the system"})
#Post('upload')
#UseInterceptors(FileInterceptor('file'))
uploadFile(#UploadedFile() file, #Body() fileInfo: UploadFileDto) {
console.log(file);
console.log(fileInfo);
}
}
and in my DTO file:
import { IsString } from 'class-validator'
import { ApiProperty } from '#nestjs/swagger'
export class UploadFileDto {
#IsString()
communityName: string
#IsString()
type: string
#ApiProperty({type:"file"})
file: any;
}
This way, it shows everything in swagger (and works) properly. However, as you can see there is a redundant "file" variable in my DTO class and function variable. If I remove the "file" attribute from the DTO, then the swagger UI doesn't recognize this parameter as the file input. If I remove it from the function parameter, then it shows up in swagger UI but the controller doesn't receive the uploaded file. Is there a way to correct this?
Thanks.
I just encountered the same problem and thanks to you, I actually solved my issue.
My controller looks as following
#Controller('files')
#ApiTags('files')
export class FilesController
{
#Post('import')
#ApiConsumes("multipart/form-data")
#UseInterceptors(FileInterceptor('file',
{
storage: diskStorage({
destination: './tmp',
filename: (req, file, cb) => {
const randomName = Array(32).fill(null).map(() => (Math.round(Math.random() * 16)).toString(16)).join('')
return cb(null, `${randomName}${extname(file.originalname)}`)
}
})
}
))
async upload(
#Body() uploadDto: UploadDto,
#UploadedFile() file: Express.Multer.File
): Promise<void>
{
console.log(importDto);
console.log(file);
}
}
And my DTO
import { IsString } from 'class-validator';
import { ApiProperty } from '#nestjs/swagger';
export class ImportDto {
#IsString()
name: string;
#ApiProperty({ type:'string', format:'binary' })
file: any;
}
Notice the only real change is in the #ApiProperty() in the DTO. That did the trick.

How To Mock Repository, Service and Controller In NestJS (Typeorm & Jest)

I'm new at typescript. My Nestjs project app is something like this. I'm trying to use repository pattern, so i separated business logic (service) and persistance logic (repository)
UserRepository
import { Injectable } from '#nestjs/common';
import { InjectRepository } from '#nestjs/typeorm';
import { Repository } from 'typeorm';
import { UserEntity } from './entities/user.entity';
#Injectable()
export class UserRepo {
constructor(#InjectRepository(UserEntity) private readonly repo: Repository<UserEntity>) {}
public find(): Promise<UserEntity[]> {
return this.repo.find();
}
}
UserService
import { Injectable } from '#nestjs/common';
import { UserRepo } from './user.repository';
#Injectable()
export class UserService {
constructor(private readonly userRepo: UserRepo) {}
public async get() {
return this.userRepo.find();
}
}
UserController
import { Controller, Get } from '#nestjs/common';
import { UserService } from './user.service';
#Controller('/users')
export class UserController {
constructor(private readonly userService: UserService) {}
// others method //
#Get()
public async getUsers() {
try {
const payload = this.userService.get();
return this.Ok(payload);
} catch (err) {
return this.InternalServerError(err);
}
}
}
How do i create unit testing for repository, service & controller without actually persist or retrieve data to DB (using mock)?
Mocking in NestJS is pretty easily obtainable using the testing tools Nest exposes is #nestjs/testing. In short, you'll want to create a Custom Provider for the dependency you are looking to mock, and that's all there is. However, it's always better to see an example, so here is a possibility of a mock for the controller:
describe('UserController', () => {
let controller: UserController;
let service: UserService;
beforeEach(async () => {
const moduleRef = await Test.createTestingModule({
controllers: [UserController],
providers: [
{
provide: UserService,
useValue: {
get: jest.fn(() => mockUserEntity) // really it can be anything, but the closer to your actual logic the better
}
}
]
}).compile();
controller = moduleRef.get(UserController);
service = moduleRef.get(UserService);
});
});
And from there you can go on and write your tests. This is pretty much the same set up for all tests using Nest's DI system, the only thing to be aware of is things like #InjectRepository() and #InjectModel() (Mongoose and Sequilize decorators) where you'll need to use getRepositoryToken() or getModelToken() for the injection token. If you're looking for more exmaples take a look at this repository

Resources