Write your own validator in less than 100 line!
Published: 2020-08-25 10:13:15

## Writing a validator is easy!
Base of a validator function is a traverse through object's properties.
Then you need to check your value with some basic `if/else` to validate input data.

The final validation schema will look like this:
```javascript
const Schema = {
    'Username': 'Required|Alphabet|Min:3|Max:10',
    'Password': 'Required|Alphabet|Min:3|Max:10',
    'Profile': {
        'Age': 'Required|Numeric|Gt:17|Lt:99',
        'Sex': 'Required|In:Male,Female',
    }
} 
```


Less talk more code, let's start with traverse function:

```javascript
enum SchemaType {
    string,
    object,
    array
}
.
.
.
class Validator {

   private MainValidator(Schema:any, Data:any,Path?:string) {
        for (const key in Schema) {
            const Rule = Schema[key];
            const Node = (Path ? Path + "." : "") + key;
            const RuleData = Data ? Data[key] : {};
            const InputSchemaType:SchemaType | undefined = this.GetSchemaType(Rule);
            switch (InputSchemaType) {
                case SchemaType.object:
                    this.MainValidator(Rule, RuleData,key);
                    break;
                case SchemaType.array:
                    this.ArrayValidator(Rule[0],RuleData,key);
                    break;
                default:
                    this.InnerValidator(Rule, RuleData,Node);
                    break;
            }
        }
    }
.
.
.
    private GetSchemaType(Rule:any):SchemaType | undefined {
        if (typeof Rule === "string") {
            return SchemaType.string;
        }
        else if (typeof Rule === "object" && !Array.isArray(Rule)) 
        {
            return SchemaType.object;
        }
        else if (typeof Rule === "object" && Array.isArray(Rule)) 
        {
            return SchemaType.array;
        }
        else {
            return;
        }
    }
}
```

`MainValidator` method is the base of our validation then we need to define the branches of the tree, `ArrayValidator` and `InnerValidator`.


```javascript
.
.
.
    private ArrayValidator(Rule:any, Data:any, key?:string) {
        let index = 0;
        do {
            this.MainValidator(Rule, Data[index] , key+"."+index);
            index++
        } while ((Data || []).length > index);
       
     }
.
.
.
    private InnerValidator(rules:any,value:any,Node:string) {
        const FnNameAndArgs = this.GetFnNameAndArgs(rules);
        for (let index = 0; index < FnNameAndArgs.length; index++) {
            const FnNameAndArg = FnNameAndArgs[index];
            const Result = this.Validators.get(FnNameAndArg.name)?.call(this,value,...FnNameAndArg.args)
            if(Result && !Result.isValid) {
                this.Errors.has(Node) ? 
                this.Errors.get(Node)?.push(Result.message) :
                this.Errors.set(Node,[Result.message]); 
            }
        }   
    }   

```

When the branches reach to the leaf mean `GetFnNameAndArgs` method. You should have the real validator methods and a place to store them.

Also because we want to use string patterns in our schema we could have a `Map` of `string -> function`

```javascript
.
.
.
    private Validators: Map<string,Function> = new Map();
    private GetFnNameAndArgs(rules: any) {
        return rules.split('|').map((item:any) => {
            const V = item.split(':');
            const name = V[0]; V.shift();
            return { name, args: V };
        });
    }
```
And finally a place to store Validation Errors

```javascript
.
.
.
    private Errors: Map<string,string[]> = new Map();
.
.
.
```


To add any kind of validator function we simply use `constructor`

```javascript
const Required = (value:any) => {
    if(value) {
        return {
            isValid: true,
        }
    } else {
        return {
            isValid: false,
            message:"Is Required"
        }
    }
}

class Validator {
    constructor() {
        this.Validators.set("Required", Required);
    }
...
}

```
And finally add an entry point for your class to do validation for input objects.

```javascript

class Validator {
...
    Validate(Schema:any, Data:any){
        this.Errors = new Map<string,string[]>();
        this.MainValidator(Schema, Data);
        return this.Errors;
    }
...
}

```

## Conclusion
Now you have a nice and clean validator, with some help of `memoization` we also can make it faster than many other validator in js world.

You can find the whole code [here](https://github.com/golkhandani/simple_validator/blob/master/validator.ts).