Search code examples
typescriptnestjs

Is it possible to add decorators dynamically in TypeScript?


I'm developing a personal project in NestJS for learning purposes. Currently I'm integrating Swagger into it. So, let's suppose I want to show that a certain route may give as response the UnauthorizedException. I would naturally have to do this

@ApiUnauthorizedResponse({ description: 'Unauthorized' })
@Get()
findAll() {
  return this.usersService.findAll();
}

But, I would like to add this decorator to all the routes that are not public. So, in an interceptor, I would get the current route handler and the isPublic metadata. From these, I would like to decide whether to add the decorator to the handler reference. But how would I do this, in case it is possible?

Right now, I imagine the interceptor to be something like this. Please disconsider the interceptor name, it is not final.

@Injectable()
export class UnauthSwaggerInterceptor implements NestInterceptor {
  constructor(private readonly reflector: Reflector) {}

  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    const handler = context.getHandler();

    const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC, [
      context.getHandler(),
      context.getClass(),
    ]);

    if (!isPublic) {
      //  Apply Swagger decorator to handler
    }

    return next.handle();
  }
}

So as can be seen, I would first get a reference to the current handler, which is of type Function.

After that, obtain the isPublic metadata from the route using a reflector.

And then, verify if the route is not public in order to apply the decorator. I mark a route as public with the public decorator.

export const IS_PUBLIC = 'isPublic';

export const Public = () => SetMetadata(IS_PUBLIC, true);

So, I would like to attach the aforementioned decorator for Unauthorized response, to this specific non-public handler. Would it be possible to do this? As it's not the function declaration, it's a reference that will be obtained during runtime. In positive case, then what would be the correct course of action?

I don't know if there is a way to directly apply the decorator to the function. Even because the decorator can only be applied in the context of a class, but I want to apply it only to controller methods. As I understand, the Swagger plugin for Nest does basically this, it adds decorators on the fly based on certain rules. This is also what I want to achieve, if possible.


Solution

  • It doesn't really make sense to try and update the decorators bound during runtime. Decorators are there so that at the start of the server, certain actions can be taken or read during runtime. In the case of the swagger decorators, those all are read during server start up through the SwaggerModule's createDocument method. Even if you were to change them at the time of request, you wouldn't modify the current swagger, and it wouldn't persist after a server restart.