Search code examples
typeormclass-validatortypescript-decorator

Custom validator via class-validator has null pointer


I have this:

    import * as val from 'class-validator';
    import {ObjectId as MongoId} from 'mongodb';
    
    @val.ValidatorConstraint({async: false})
    export class IsTraceValidConstraint implements val.ValidatorConstraintInterface {
      validate(trace: any, args: val.ValidationArguments) { 
        return trace.deviceId && typeof trace.deviceId === 'string'; // << trace is undefined
      }
    
      defaultMessage(args: val.ValidationArguments) {
        return 'Trace validation failed';
      }
    }
    
    // Function to create the class-level decorator
    export function IsTraceValid(validationOptions?: val.ValidationOptions) {
      return function (target: Function) {
        val.registerDecorator({
          target: target,
          options: validationOptions,
          constraints: [],
          validator: IsTraceValidConstraint,
          name: 'isTraceValid',
          propertyName: 'isTraceValid',
        });
      };
    }

then I use it like so:

    @IsTraceValid()
    @t.Unique("user-hash-device", ["hash", "userId", "deviceId"])
    @t.Entity('vibe_trace')
    export class Trace extends From<Trace> { }

but if I run it, I get a null pointer -

trace is undefined in validate callback - so trace.deviceId throws an error

Clearly I am doing something wrong - but what?


Solution

  • Update propertyName in your decorator registration to match the name of the property you want to validate in your custom constraint. i.e. 'deviceId'.

    In your custom constraint you now check directly against that value in your validate method. If you want to check against other properties you can access the trace instance via args.object

    import * as val from 'class-validator';
    import { ObjectId as MongoId } from 'mongodb';
    
    @val.ValidatorConstraint({ async: false })
    export class IsTraceValidConstraint implements val.ValidatorConstraintInterface {
      validate(value: any, args: val.ValidationArguments) {
        // trace instance is accessible via `args.object`
        return typeof value === 'string';
      }
    
      defaultMessage(args: val.ValidationArguments) {
        return 'Trace validation failed';
      }
    }
    
    // Function to create the class-level decorator
    export function IsTraceValid(validationOptions?: val.ValidationOptions) {
      return function (target: Function) {
        val.registerDecorator({
          target: target,
          options: validationOptions,
          constraints: [],
          validator: IsTraceValidConstraint,
          name: 'isTraceValid',
          propertyName: 'deviceId',
        });
      };
    }