Skip to content

Instantly share code, notes, and snippets.

@josephdpurcell
Created September 7, 2022 14:14
Show Gist options
  • Save josephdpurcell/fc04cfd428a6ee9d7ffb64685e4fe3a6 to your computer and use it in GitHub Desktop.
Save josephdpurcell/fc04cfd428a6ee9d7ffb64685e4fe3a6 to your computer and use it in GitHub Desktop.

Revisions

  1. josephdpurcell created this gist Sep 7, 2022.
    25 changes: 25 additions & 0 deletions my-decorator.ts
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,25 @@
    import { ArgumentMetadata, createParamDecorator, ExecutionContext } from '@nestjs/common';
    import { METADATA__PARAM_TYPE } from '../constants'; // METADATA__PARAM_TYPE = 'myType'
    import { paramTypeEnhancer } from './param-type.enhancer';

    export const MyDecorator = createParamDecorator(
    async (data: unknown, ctx: ExecutionContext): Promise<any> => {
    const metatype = Reflect.getOwnMetadata(METADATA__PARAM_TYPE, ctx.getHandler());

    // We build the argument metadata that @nestjs/class-validator is expecting.
    // Specifically, we specify the type as "custom" -- this is a special value
    // coming from NestJS and all custom decorators are "custom". The data is
    // left undefined (only a @Query or @Param type would have data). Last, the
    // metatype is the reference to the type.
    const argument: ArgumentMetadata = {
    type: 'custom',
    data: undefined,
    metatype: metatype,
    };

    // Do processing here. You can even return a promise.
    // Often you want access to the request which can be done like this:
    const request = ctx.switchToHttp().getRequest();
    },
    [paramTypeEnhancer],
    );
    22 changes: 22 additions & 0 deletions param-type.enhancer.ts
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,22 @@
    import { ParamDecoratorEnhancer } from '@nestjs/common';
    import { METADATA__PARAM_TYPE } from '../constants'; // METADATA__PARAM_TYPE = 'myType'

    /**
    * This enhancer grabs the Typescript type of the parameter and shoves it into
    * the metadata. We do this because the CustomParamFactory does not have access
    * to the type of param.
    */
    export const paramTypeEnhancer: ParamDecoratorEnhancer = (
    target: Record<string, unknown>,
    propertyKey: string,
    parameterIndex: number,
    ): void => {
    // Typescript adds the "design:paramtypes" metadata with an array of class
    // types where the keys are the method argument index and the value is the
    // class type.
    const paramTypes = Reflect.getOwnMetadata('design:paramtypes', target, propertyKey);
    // We can use the parameterIndex to retrieve the specific type we want.
    const metatype = paramTypes[parameterIndex];
    // Now, we assign the parameter type to the metadata at a key we know.
    Reflect.defineMetadata(METADATA__PARAM_TYPE, metatype, target[propertyKey]);
    };