#Dynamic controller not working properly

1 messages · Page 1 of 1 (latest)

modest scarab
#

I have created the following function for creating a dynamic controller:

import type { Type } from '@nestjs/common';
import { Body, Controller, Post } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import type { ObjectLiteral, DeepPartial } from 'typeorm';
import { Repository } from 'typeorm';

type Constructor<I> = new (...args: unknown[]) => I; // Main Point

export function dynamicController<T extends ObjectLiteral, D extends DeepPartial<T>>(
  entity: Constructor<T>,
): Type {
  @Controller(entity.name.toLowerCase())
  class DynamicController {
    constructor(
      @InjectRepository(entity)
      private readonly repository: Repository<T>,
    ) {}

    @Post()
    async createEntity(@Body() dtoBody: D): Promise<T> {
      const newEntity = this.repository.create({ ...dtoBody });
      const savedEntity = await this.repository.save(newEntity);
      return savedEntity;
    }
  }

  return DynamicController;
}

However, if for instance I create a controller using dynamicController<Patient, PatientDto>(Patient) what happens is it does sucessfully register a POST endpoint but dtoBody parameter isn't actually being validated against PatientDto..
Is there any way to solve this?

#

After further checking it seems that if a type hasn't been explicity defined, it doesn't emit the metadata correctly and instead just considers it of type Object...
Seems like a bug maybe?

#

For instance, this fails too:

type D = DeviceDto;

@UseGuards(AuthGuard('bearer'))
@Controller('pump')
export class DeviceController {
  constructor(
    @InjectRepository(Device)
    private readonly deviceRepository: Repository<Device>,
  ) {}

  @Post()
  async updateDeviceData(@Body() body: D, @Res() res: Response): Promise<void> {

But this:

type D = DeviceDto;

@UseGuards(AuthGuard('bearer'))
@Controller('pump')
export class DeviceController {
  constructor(
    @InjectRepository(Device)
    private readonly deviceRepository: Repository<Device>,
  ) {}

  @Post()
  async updateDeviceData(@Body() body: DeviceDto, @Res() res: Response): Promise<void> {

Works fine

hallow sparrow
#

Seems like a bug maybe
In typescript.. As soon as you make a class reference into a type or interface, you lose runtime reflection features

modest scarab
#

Is there any way to achieve what I want then?

hallow sparrow
#

Hm, probably by setting the metadata manually with @SetMetadata and passing the class reference in. You'll gotta figure out what the metadata should look like though

modest scarab
#

I noticed on a controller I made that does work it shows:

`__metadata("design:paramtypes", [DeviceDto, Object]),`

So I tried @SetMetadata('design:paramtypes', [dto.prototype, Object]) on the generic method but that doesn't work :\

modest scarab
#

Any help possibly?

#

Ok I made it work, I just needed to do a new ValidationPipe inside the body decorator and define the type there