napfernandes
napfernandes

Reputation: 1359

Dynamic NestJS controller methods

I'm trying to create some dynamic methods to a controller in NestJS, without much success. Basically, for every "command" declaration that I have, I want to create the same "implementation", but in a different "endpoint".

This is my generic controller:

@Controller()
export class DynamicCommandController {
  handleFunction(@Req() req: FastifyRequest, @Res() response: FastifyReply) {
    console.log(`Handle function...`);
    console.log({ req, response });

    response
      .status(200)
      .header('Content-Type', 'application/json')
      .send({ hello: 'world' });
  }
}

My generic implementation is as it follows:

const decorators = {
      GET: Get,
      PUT: Put,
      POST: Post,
      PATCH: Patch,
      DELETE: Delete,
    };

    dynamicCommands.map(({ command }) => {
      const endpointDefintion = command.prototype.endpointDefinition();
      if (!endpointDefintion) {
        return;
      }

      if (this.registeredRoutes.has(endpointDefintion.path)) {
        return;
      }

      const currentDecorator = decorators[endpointDefintion.method];

      if (!currentDecorator) {
        return;
      }

      currentDecorator(endpointDefintion.path)(
        DynamicCommandController.prototype,
        'handleFunction',
        Object.getOwnPropertyDescriptor(
          DynamicCommandController.prototype,
          'handleFunction',
        ),
      );

      this.registeredRoutes.add(endpointDefintion.path);
    });

I can decorate my methods, but I can't create two @Get for the same function, they get overwriten.

If I try to "copy" the handleFunction method for every "command", then the implementation looks like the following:


    dynamicCommands.map(({ command }) => {
      const endpointDefintion = command.prototype.endpointDefinition();
      if (!endpointDefintion) {
        return;
      }

      if (this.registeredRoutes.has(endpointDefintion.path)) {
        return;
      }

      const currentDecorator = decorators[endpointDefintion.method];

      if (!currentDecorator) {
        return;
      }

      const methodName = `handleFunction_${command.prototype.commandName}${command.prototype.version}`;

      // Dynamically create a new method for the endpoint
      const dynamicMethod = async function (
        request: FastifyRequest,
        response: FastifyReply,
      ) {
        console.log(`Handle function...`);
        console.log({ request, response });

        response
          .status(200)
          .header('Content-Type', 'application/json')
          .send({ hello: 'world' });
      };

      // Copy the method
      Object.defineProperty(DynamicCommandController.prototype, methodName, {
        value: dynamicMethod,
        writable: true,
        configurable: true,
      });

      applyDecorators(Req)(
        DynamicCommandController.prototype,
        methodName,
        Object.getOwnPropertyDescriptor(
          DynamicCommandController.prototype,
          methodName,
        ),
      );

      currentDecorator(endpointDefintion.path)(
        DynamicCommandController.prototype,
        methodName,
        Object.getOwnPropertyDescriptor(
          DynamicCommandController.prototype,
          methodName,
        ),
      );

      this.registeredRoutes.add(endpointDefintion.path);
    });

The methods are created, but somehow request and response are undefined.

How can I generate these methods, properly decorated with @Req and @Res?

Upvotes: 0

Views: 51

Answers (0)

Related Questions