Considering that when we talk about programming, much of the code developed by developers is equivalent to data validations, in this article I will look at ways to apply code quality concepts such as reuse, code reduction, and clean code through the light-validate library on client / server layers using Typescript as the programming language.
About Light Validate
https://www.npmjs.com/package/light-validate
Light Validate is a library that allows the developer, to create data validations, and to use them in model class mapping through Annotations / Decorators to automate the miscellaneous data validation routine, allowing for a similar implementation to existing implementations. used in Java languages through HIBERNATE / SPRINGDATA, or C # through EntityFramework or similar libraries.
Some important features:
- Framework Agnostic.
- Functional Paradigm.
- Asynchronous Paradigm.
Conceptual
Creating Validations (Light Rules)
Validations that can be interpreted by Light Validate are basically functions that implement the Light Rule interface.
LightRule Interface
The LightRule interface represents a void Function that receives 2 parameters, which are value, and target.
- Value Parameter, this parameter represents the value that will be validated.
- Target Parameter, this parameter represents the object containing the value parameter. If the value to be validated is invalid, the function must throw via throw a code, or error message. If the value to be validated is valid, the function should neither post nor return anything.
Creating Mappings (Light Mapping)
Mappings that are interpreted by Light Validate are basically classes, with decorators LightValidate in decorating properties whose values are to be validated.
Decorator LightValidate
Decorator LightValidate is basically a function that receives as parameters the validations that should validate the property whose is decorated with decorator LightValidate.
Validating Objects
To validate an object, Light Validate provides some functions, which are basically functions that take as parameters, target and Klass, and return a promise.
If there are any errors, the promise will be rejected with a vector object that implements the interface LightException
- Target Parameter, this parameter represents the object that will be validated.
- Klass Parameter, this parameter represents the mapping that should be used to validate the Target parameter.
Interface Light Exception
The Interface has 4 properties, aiming to represent exception data generated by validation, which are as follows:
- rule, The Rule that generated the exception.
- target, The Object that was validated and raised the exception.
- property, The property whose value raised the exception.
- code, The Code thrown by Rule as an exception.
Validate Function
The validate function is used to validate only 1 or multiple objects from a mapping.
validateOne Function
The validate function is used to validate only 1 object from a mapping.
validateEach Function
The validate function is used to validate multiple objects from one mapping.
In practice
Let's consider the following concept to show in practice how the library works:
You must create a screen that enrolls Students in an Educational Institution.
The Student Template, which will be sent to the server, is represented by the following interface:
/* student.model.ts */
export interface StudentModel {
name:string;
document:string;
birthDate:string;
phone:string;
state:string;
country:string;
}
Consider that the system requester specifies that the fields must have the following validations
- name: must not have numeric characters, must have at least 3 characters, and at most 40 characters.
- document: must have 11 characters.
- birthDate: must have the format ## / ## / ####.
- phone: Must allow 9 digit phone number.
- state: must have only one of the following values: Sao Paulo, Minas Gerais, or Rio de Janeiro, New York, Hollywood, Kansas
- country: must have only one of the following values: Brazil for if the state field is equivalent to São Paulo, Minas Gerais or Rio de Janeiro, and United States for the others.
Installation
Run the following commands for installing the library:
$ npm install -save light-validate
$ npm install -save reflect-metadata
Creating Validations (Light Rules)
Field Name: NameLightRule
/* name.light-rule.ts */
import { LightRule } from 'light-validate';
export const NameLightRule:LightRule = async function (value:string, target:any) {
if(value){
if(!new RegExp("/^[A-Za-z\s]+$/").test(value)){
throw 'only-alphabetical-characters';
}
if(value.length<3) {
throw 'min-3-characters';
}
if(value.length>40) {
throw 'max-40-characters';
}
}
}
Field Document: DocumentLightRule
/* document.light-rule.ts */
import { LightRule } from 'light-validate';
export const DocumentLightRule:LightRule = async function (value:string, target:any) {
if(value) {
if(value.length!=11) {
throw 'must-be-11-numbers';
}
}
}
Field BirthDate: BirthDateLightRule
/* birth-date.light-rule.ts */
import { LightRule } from 'light-validate';
export const BirthDateLightRule:LightRule = async function (value:string, target:any) {
const values = value.split('/');
if(values.length!=3) {
throw 'invalid-date';
}
if(values[0].length==2) {
throw 'invalid-date';
}
if(values[1].length==2) {
throw 'invalid-date';
}
if(values[2].length==4) {
throw 'invalid-date';
}
}
Field Phone: PhoneLightRule
/* phone.light-rule.ts */
import { LightRule } from 'light-validate';
export const PhoneLightRule:LightRule = async function (value:string, target:any) {
if(value.length!==9){
throw 'invalid-phone';
}
}
Field State: StateLightRule
/* state.light-rule.ts */
import { LightRule } from 'light-validate';
export const StateLightRule:LightRule = async function (value:string, target:any) {
if(['São Paulo','Minas Gerais','Rio de Janeiro','New York','Hollywood','Kansas'].indexOf(value)===-1){
throw 'invalid-state';
}
}
Field Country: CountryLightRule
/* country.light-rule.ts */
import { LightRule } from 'light-validate';
export const CountryLightRule:LightRule = async function (value:string, target:any) {
if(value==='Brasil' && ['São Paulo','Minas Gerais','Rio de Janeiro'].indexOf(target['state'])===-1){
throw 'invalid-country';
}
if(value==='United States' && ['New York','Holywood','Kansas'].indexOf(target['state'])===-1){
throw 'invalid-country';
}
}
Creating Mappings (Light Mapping)
Below is the class that will represent validation mapping to validate objects that follow its format.
/* student.light-mapping.ts */
import { StudentModel } from './student.model';
import { NameLightRule } from './name.light-rule';
import { DocumentLightRule } from './document.light-rule';
import { BirthDateLightRule } from './birth-date.light-rule';
import { PhoneLightRule } from './phone.light-rule';
import { StateLightRule } from './state.light-rule';
import { CountryLightRule } from './country.light-rule';
import { LightValidate } from 'light-validate';
export class StudentLightMapping implements StudentModel {
/*
All properties that are decorated with the @LightValidate decorator
must be initialized with undefined, or null, otherwise they will be ignored.
*/
@LightValidate(NameLightRule)
public name:string = undefined;
@LightValidate(DocumentLightRule)
public document:string = undefined;
@LightValidate(BirthDateLightRule)
public birthDate:string = undefined;
@LightValidate(PhoneLightRule)
public phone:string = undefined;
@LightValidate(StateLightRule)
public state:string = undefined;
@LightValidate(CountryLightRule)
public country:string = undefined;
}
Validando Objetos
import { StudentModel } from './student.model';
import { StudentLightMapping } from './student.light-mapping';
import { validate } from 'light-validate';
const model: StudentModel = {
name:'John Deer',
document:'444111222',
birthDate:'2019-12-12',
phone:'111133',
state:'New York',
country:'Brazil'
}
validate(model,StudentLightMapping)
.then(()=>console.log('No exceptions found'))
.catch((exceptions:LightException[])=>
exceptions.forEach((exception:LightException)=>
console.log(`validation ${exception.rule}, target object ${e.target}, property ${exception.property}, code ${e.code}`)
)
)
);
Creating Reusable Validations
As you can see, all the validations I created in this guide can be optimized and separated so that they can be reused, such as:
NameLightRule broken into 3 validations:
OnlyAlphaLightRule, which validates a field that should only receive alphabetic characters.
/* only-alpha.light-rule */
import { LightRule } from 'light-validate';
export const OnlyAlphaLightRule:LightRule = async function (value:string, target:any) {*/
if(value){
if(!new RegExp("/^[A-Za-z\s]+$/").test(value)){
throw 'only-alphabetical-characters';
}
}
}
MinLengthLightRule, which validates a field that must receive a value that has at least X characters.
/* min-length.light-rule */
export const MinLengthLightRule:LightRule = function(length:number) {
return async function (value:string, target:any) {*/
if(value && value.length<length) {
throw `min-${length}-characters`;
}
}
}
MaxLengthLightRule, which validates a field that must receive a value that has a maximum of X characters.
/* max-length.light-rule */
export const MaxLengthLightRule:LightRule = function(length:number) {
return async function (value:string, target:any) {*/
if(value && value.length>length) {
throw `max-${length}-characters`;
}
}
}
Thus, we have already been able to eliminate 3 specific validations through the use of 3 generic validations, which can be reused in various situations.
import { StudentModel } from './student.model';
import { OnlyAlphaLightRule } from './only-alpha.light-rule';
import { MinLengthLightRule } from './min-length.light-rule';
import { MaxLengthLightRule } from './max-length.light-rule';
import { BirthDateLightRule } from './birth-date.light-rule';
import { StateLightRule } from './state.light-rule';
import { CountryLightRule } from './country.light-rule';
import { LightValidate } from 'light-validate';
export class StudentLightMapping implements StudentModel {
/*
All the properties that most adorn with the @LightValidate decorator
must be initialized to undefined or null otherwise they will be ignored.
*/
@LightValidate(OnlyAlphaLightRule,MinLengthLightRule(3),MaxLengthLightRule(40))
public name:string = undefined;
@LightValidate(MinLengthLightRule(11),MaxLengthLightRule(11))
public document:string = undefined;
@LightValidate(BirthDateLightRule)
public birthDate:string = undefined;
@LightValidate(MinLengthLightRule(9),MaxLengthLightRule(9))
public phone:string = undefined;
@LightValidate(StateLightRule)
public state:string = undefined;
@LightValidate(CountryLightRule)
public country:string = undefined;
}
Using with SPA Frameworks
The Light Validate library has some abstractions for some industry frameworks that provide support for Typescript, which are:
These are basically policy modules that communicate with the Light Validate library to perform validations, and display exceptions similar to Jquery Validate.
Top comments (0)