Search code examples
graphqltypeormtypegraphql

TypeGraphql - @inputtype on typeorm


Hello I need to check if there is an email in the database already:

with this:

return User.findOne({ where: { email } }).then((user) => {
  if (user) return false;
  return true;
});

I have the following inputtypes:

@InputType()
export class RegisterInput {
  @Field()
  @IsEmail({}, { message: 'Invalid email' })
  email: string;

  @Field()
  @Length(1, 255)
  name: string;

  @Field()
  password: string;
}

I would like to know if there is any way for me to validate the email in the inputtype? or just in my resolve:

@Mutation(() => User)
  async register(
    @Arg('data')
    { email, name, password }: RegisterInput,
  ): Promise<User> {
    const hashedPassword = await bcrypt.hash(password, 12);

    const user = await User.create({
      email,
      name,
      password: hashedPassword,
    }).save();

    return user;
  }

Solution

  • Actually you can register your own decorator for class-validator

    For example it can look something like this:

    isEmailAlreadyExists.ts

    import {
      registerDecorator,
      ValidationOptions,
      ValidatorConstraint,
      ValidatorConstraintInterface,
    } from 'class-validator';
    import { UserRepo } from '../../repositories/UserRepo';
    import { InjectRepository } from 'typeorm-typedi-extensions';
    
    @ValidatorConstraint({ async: true })
    export class isEmailAlreadyExist
      implements ValidatorConstraintInterface {
      @InjectRepository()
      private readonly userRepo: UserRepo;
    
      async validate(email: string) {
        const user = await this.userRepo.findOne({ where: { email } });
    
        if (user) return false;
        return true;
      }
    }
    
    export function IsEmailAlreadyExist(validationOptions?: ValidationOptions) {
      return function (object: Object, propertyName: string) {
        registerDecorator({
          target: object.constructor,
          propertyName: propertyName,
          options: validationOptions,
          constraints: [],
          validator: isEmailAlreadyExist,
        });
      };
    }
    

    If you're injecting dependencies than you should in inject it in class-validator too. Simply add to your main file this:

    import { Container } from 'typedi';
    import * as classValidator from 'class-validator';
    
    classValidator.useContainer(Container);
    
    ...
    const schema = await buildSchema({
        resolvers: [...],
        container: Container,
      });
    

    Then you can use decorator in your InputType

    import { InputType, Field } from 'type-graphql';
    import { IsEmailAlreadyExist } from '../../../utils/validators/isEmailAlreadyExist';
    
    @InputType()
    export class YourInput {
      @Field()
      @IsEmailAlreadyExist()
      email: string;
    }