Can decorators be dynamically added in TypeScript?

As I work on my personal project in NestJS for educational purposes, integrating Swagger has become a key focus. I want to showcase that a specific route could potentially result in an UnauthorizedException response. To achieve this, I need to add the following code snippet:

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

However, I aim to streamline this process by applying the decorator to all non-public routes. My approach involves creating an interceptor that analyzes the current route handler and its metadata on whether it is public or not. If it's not public, then the decorator should be added dynamically to the handler reference. But how can this be achieved effectively?

The envisioned interceptor concept is showcased below, albeit with temporary naming:

@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();
  }
}

This plan involves first identifying the current handler, followed by extracting the isPublic metadata from the route using a reflector. Subsequently, verifying if the route is indeed not public to apply the necessary decorator. Routes marked as public are designated with the public decorator.

export const IS_PUBLIC = 'isPublic';

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

Ultimately, the objective is to append the aforementioned Unauthorized response decorator to these specific non-public handlers. Is such a task feasible? Since it involves runtime references rather than function declarations, the correct course of action remains uncertain.

There exists ambiguity on directly applying the decorator to the function itself, especially since decorators typically pertain to classes but in this scenario, they should only affect controller methods. The Nest Swagger plugin seemingly accomplishes similar functionality by dynamically adding decorators based on predefined rules. This desired outcome aligns with my own objectives, assuming it can be realized.

Answer №1

Attempting to update decorators during runtime is not a practical approach. Decorators are designed to execute specific actions at the beginning of the server's operation, rather than being dynamically updated during runtime. For example, Swagger decorators are processed and documented at server startup using the SwaggerModule's createDocument method. Any changes made to these decorators during a request will not alter the current swagger configuration and will not persist beyond a server restart.

Answer №2

A decorator, according to the TypeScript manual, is essentially a function that is invoked with the object being decorated (although in this case, it's referred to as a decorator factory):

function Decorator(s: string) {
  return (target: any) => {
    console.log("Decorating", target);
    target.foo = s;
    return target;
  };
}

// Decorator syntax
@Decorator("wibba wubba")
class SomeClass {}

// Call syntax
const OtherClass = Decorator("webbo wobbo")(class OtherClass {});

console.log((SomeClass as any).foo);
console.log((OtherClass as any).foo);

This will output

"wibba wubba" 
"webbo wobbo" 

as expected.

In other words,

@Injectable()
class Foo {}

is roughly equivalent to

const Foo = Injectable()(class Foo{});

and you can think of a variable holding a function in place of Injectable().

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

I'm struggling to get Router.push to redirect me on Next.js with an Express server

I'm currently working on creating a simple login page with a dashboard using an Express server and Nextjs. The goal is for users to be redirected to the dashboard after successfully logging in with their credentials. However, it seems that when I use ...

What is the correct data type for the vuex store that is passed to the vuex plugin when it is being initialized?

Here is how the store initialization process is currently being implemented: /* Logic */ import Vue from 'vue' import Vuex, { StoreOptions } from 'vuex' Vue.use(Vuex) const DataManager = new UserDataManager() type RootStateType = { ...

Problem encountered while directing to a component within Angular

Here is the overview of my directory structure: Directory Structure login.component.ts: import { Component, OnInit } from '@angular/core'; import { FormBuilder, FormControl, FormGroup, Validators, ReactiveFormsModule } from '@angular/forms ...

The CORS policy has prevented access to XMLHttpRequest at 'http://localhost:3001/auth/register' from the origin 'http://localhost:3000'

Hello, I am currently working on a project using NestJs to create API endpoints and NextJs for the Authentication system. However, I encountered an error when submitting a form from NextJs: API END POINT: http://localhost:3001/auth/register const onSubmit ...

Angular 5 offers the capability to use mat-slide-toggle to easily display and manipulate

I am experiencing an issue with displaying data in HTML using a mat-slide-toggle. The mat-slide-toggle works correctly, but the display does not reflect whether the value is 1 (checked) or 0 (unchecked). Can anyone provide some ideas on how to resolve this ...

The initial update of the view does not occur when a component property changes in Angular 2 RC6

I am currently facing an issue with a component in my project. This component calls a service to retrieve locally stored JSON data, which is then mapped to an array of objects and displayed in the component view. The problem I am encountering is that the v ...

Exploring the effectiveness of testing Svelte components

Looking to test a component that utilizes a third-party module without mocking the imported components? Check out this example: // test.spec.ts import Component from "Component"; describe('Component', () => { test('shoul ...

Why am I encountering an issue while trying to access req.user.id?

Having set up passport authentication on my express, node project, I encountered an error when trying to access req.user. The error message displayed is Property 'id' does not exist on type 'User'.ts(2339). Below is the relevant code sn ...

Module Not Found Error: Electron and Typescript Collaboration

I am currently facing an issue while attempting to build my electron application using typescript generated from the electron-quick-start-typescript project. I have included an additional module named auth.ts, but unfortunately, it is not being recognized ...

Find the length of time in Typescript (measured in hours, minutes, and seconds)

Trying to calculate the duration between two dates in TypeScript (Angular): 2021-11-19 21:59:59 and 2021-11-19 22:00:18 let startDate: Date = new Date(start); let endDate: Date = new Date(end); if(end != null) { let duration = new Date(endDate.getT ...

Tips for retrieving items from <ng-template>:

When the loader is set to false, I am trying to access an element by ID that is located inside the <ng-template>. In the subscribe function, after the loader changes to false and my content is rendered, I attempt to access the 'gif-html' el ...

The type 'MenuOptions[]' cannot be assigned to type 'empty[]'

Even after numerous attempts, I am still grappling with TypeScript problems. Currently, I am at a loss on how to resolve this particular issue, despite all the research I have conducted. The code snippet below is what I am working with, but I am struggling ...

Troubleshooting: Unable to Open Page with Google Material Button in Angular 5

Currently, I'm facing an issue with a button that is not opening to a new site despite following what seems like simple steps. <button mat-raised-button href="https://www.google.com/" color="primary">Connect with Stripe</button> I even a ...

What's the deal with the Zod getter function?

Is it possible to create a getter function within a Zod object? For instance, in ES5 you can achieve the following: const person = { firstName: "John", lastName: "Doe", get fullName() {return `${this.firstName} ${this.lastName}`} } person.fullNam ...

Prettier mandates that single-line if-statements without curly braces must be written on the same line

Having recently delved into the EsLint documentation, I've adopted the curly rule set to warning for instances of multiple or nested rows of statements within conditionals. "rules": { "curly":["warn", "multi-or-nes ...

Incorporate a 'Select All' functionality into ion-select by adding a dedicated button

Looking for a way to set custom buttons on ion-select through interfaceOptions in ionic 4? HTML <ion-item> <ion-label>Lines</ion-label> <ion-select multiple="true" [(ngModel)]="SelectedLines" [interfaceOptions]="customAlertOption ...

What is the process for specifying the type of router in typescript?

I am currently working with a router array and I would appreciate some TypeScript tips when adding route items. Currently, I am receiving tips on addition but encountering an error when using props.navigate. The component shows an error if not set as any. ...

Utilizing React with Typescript to create JSX text translation files

My task involves translating text stored in a file... ///translations.txt const TEXT: { [x: string]: { [y: string]: string } } = { en: { joinNow: <React.Fragment>Join <b>Now<b/></React.Fragment>, signUp: <React.Fragmen ...

Issues with hydrating React local storage hook in custom implementation within NextJS

Currently facing an issue while implementing the localstorage hook in NextJS. The error message I am encountering is: Error: Hydration failed because the initial UI does not match what was rendered on the server.. Any suggestions on what might be causing ...

Lazy-loaded modules in Angular that contain services provided within the module

Currently, I am facing a challenge with lazy-loaded modules and services that are provided in these modules. My folder structure looks like this: app -> featureModule1 (lazy loaded) -> featureModule2 (lazy loaded) -->services --->servi ...