Tymur Valiiev
Tymur Valiiev

Reputation: 727

How to create decorator in nest.js?

I'm having troubles with this for quite a while. My decorator is supposed to give information for checking field for unique value. It looks like so:

export const IsUnique = (
  metadata: {
    entity: any,
    field: string,
  },
): PropertyDecorator => {
  return createPropertyDecorator(constants.custom_decorators.is_unique, metadata);
};

And my validation looks like so:

import { HttpException } from '@nestjs/common';
import { PipeTransform, Pipe, ArgumentMetadata, HttpStatus } from '@nestjs/common';
import {validate, ValidationError} from 'class-validator';
import { plainToClass } from 'class-transformer';
import constants from '../../../constants';

@Pipe()
export class ValidationPipe implements PipeTransform<any> {
  async transform(value, metadata: ArgumentMetadata) {
    console.log(arguments);
    const { metatype } = metadata;
    if (!metatype || !this.toValidate(metatype)) {
      return value;
    }

    const object = plainToClass(metatype, value);
    const errors = await validate(object);
    const myErrors = await this.uniqueValidation(object);
    if (errors.length > 0) {
      throw new HttpException(errors, HttpStatus.BAD_REQUEST);
    }
    return value;
  }

  private toValidate(metatype): boolean {
    const types = [String, Boolean, Number, Array, Object];
    return !types.find((type) => metatype === type);
  }

  private async uniqueValidation(object): Promise<ValidationError[]|null>{
    const md = Reflect.getMetadata('swagger/apiModelPropertiesArray', object);
    console.log(md);

    return null;
  }
}

After executing code, looks like md=undefined. So how I can retrieve my metadata? Maybe, I'm using createPropertyDecorator in a wrong way?

EDIT: After couple of hours I realized that nestjs doesn't have createPropertyDecorator, I've imported it from swagger module (BIG mistake). So I need to create my own function. Now I'm doing it like so:

export const IsUnique = (
  metadata: {
    entity: any,
    field: string,
  },
): PropertyDecorator => {
  return (target: object, propertyKey: string) => {
    const args = Reflect.getMetadata(constants.custom_decorators.is_unique, target, propertyKey) || {};
    const modifiedArgs = Object.assign(args, { IsUnique: metadata.field });
    Reflect.defineMetadata(constants.custom_decorators.is_unique, modifiedArgs, target);
  };
};

So, my question is the same - how to properly define metadata, so it never interference with others?

Upvotes: 6

Views: 9399

Answers (1)

Omkar Yadav
Omkar Yadav

Reputation: 543

I use different approach for this I use class validator

export class SignInUser {
    @IsEmail()
    email: string;

    @Length(6, 50)
    password: string;
}

and In the controller

signIn(@Body(new ValidationPipe()) signIn: SignInUser) {}

and it works like a charm

NOTE: I use ValidationPipe from @nestjs/common

and you can create their own decorators for class validator

https://github.com/typestack/class-validator#custom-validation-decorators

Upvotes: 3

Related Questions