A client and backend agnostic validation library created by NgServe.io.
Read More at Angular Tutorial - Sharing Validations Between Angular and NestJs
Run nx test shared-validator
to execute the unit tests via Jest.
This library provides the ability to validate models with the consuming service defining validations for the model provided. It is meant to help bridge the gap more easily between back and front end code as models on both sides need validation in a reusable way.
Name | Returns | Description |
---|---|---|
ValidationErrors |
{ [validationName: string]: unknown } |
The validation type and the values associated with that validation e.g. { required: true }
|
PropertyValidationErrors<T> |
Record<keyof T, unknown> |
Provides a key on property T of unknown type which will be the return validation. e.g. {firstName: { required: true, maxLength: { max: 50, actual: 51 } }
|
ValidatorFn<T> |
ValidationErrors |
A function that returns the validations per the validator on a property |
ValidationField<T> |
(item: Partial<T>) => unknown |
A delegate method that returns the value to be validated. e.g. (item: { name: string }) => item.name
|
PropertyValidators<T> |
Record<keyof T, ValidatiorFn<T>>[] |
A list of validators of a particular property on T e.g. { firstName: [ required, max(10) ], email: { email, maxLength(50) } }
|
Validator<T> |
(value: ValidationField<T>) => ValidatorFn<T> |
A validator that chooses the value in which to validate per the delegate ValidationField<T> . |
ModelValiatorFn<T> |
(item: Partial<T>, partial?: boolean) => PropertyValidationErrors<T> or Null |
A validate function that can choose to do partial validations depend on what's the model returns |
Validation Errors refers to the ValidationErrors
return when the validation is executed.
Validator | Validation Errors | Description |
---|---|---|
keys<T>(fn: ValidationField<T>): ValidatorFn<T> |
{keys: false} |
Checks if the item being validated has any keys e.g. Object.keys({name: 'Steve'}).length > 0
|
required<T>(fn: ValidationField<T>): ValidatorFn<T> |
{ required: true} |
Checks the value is not null or undefined and is not empty or white space. |
emptyOrWhiteSpace<T>(fn: ValidationField<T>): ValidatorFn<T> |
{ emptyOrWhiteSpace: true } |
Checks if the provided value is empty or white space |
lessThan(compareValue: number): Validator<T> |
{ lessThan: { compareValue: number, actual: number } } |
Checks if the itemValue is greater than the compareValue supplied in the validation. |
greaterThan(compareValue: number): Validator<T> |
{ greaterThan: { compareValue: number, actual: number } } |
Checks if the itemValue is less than the compareValue supplied in the validation. |
email<T>(fn: ValidationField<T>): ValidationFn<T> |
{ email: false } |
Checks if the provided value is an email address. |
url<T>(fn: ValidationField<T>): ValidatorFn<T> |
{url: false} |
Checks if the value provided is a URL |
minLength<T>(number): Validator<T> |
{ minLength: { compareValue: number, actual: number } } |
Checks the min length required of the compareValue with itemValue for a property in the validation |
pattern<T>(pattern: string): ValidatorFn<T> |
{ pattern: { regEx: string, value: StringOrNumber } } |
Checks the pattern of the supplied field value. |
maxLength<T>(compareValue: number) |
{ maxLength: { compreValue: number, actual: number } } |
Checks the string value length being supplied isn't over the compareValue
|
One validate<T>
method exists to help validate a model.
Method | Description |
---|---|
validate<T>(validatorFns: PropertyValidators<T>): (item: Partial<T>) => ModelValidatorFn<T> |
Returns a delegate method for a model to be validated against. Validates partial for patch updates.* |
propertyValidators<T>(fn: ValidationField<T>, fns:(validationField: ValidationField<T>) => ValidatorFn<T>)[]) |
A helper method that has the validationField<T> supplied and an array of ValidatorFns<T>
|
- Keep in mind if the object your checking in your
validatorFns
doesn't contain one of the properties on a partial check this will cause an error, so be careful with partial checks.
type ModelT = {
firstName: string;
email: string;
url: string;
jerseyNumber: number | null;
};
const model: ModelT = {
firstName: 'Steve',
email: 's@s.com',
url: 'https://detroitredwings.com',
jerseyNumber: null,
};
const modelTValidator: (item: T) => PropertyValidationErrors<T> =
validate<ModelT>({
firstName: propertyValidators((p) => p.firstName, [required]),
email: propertyValidators((p) => p.email, [required, email]),
url: propertyValidators((p) => p.url, [url]),
jerseyNumber: propertyValidators((p) => p.jerseyNumber, [required]),
});
modelTValidator(model); // { jerseyNumber: { required: true } }
Reference below as to how to write a custom validator.
// This is a Validator<T>
export function emptyOrWhiteSpace<T>(
value: ValidationField<T>
): ValidatorFn<T> {
return (item: T): ValidationErrors => {
const itemValue = value(item);
return isEmptyOrWhiteSpace(itemValue as string)
? ({ emptyOrWhiteSpace: true } as Record<string, unknown>)
: null;
};
}
// Returns a Validator<T>
export function lessThan<T>(compareValue: number): Validator<T> {
return (value: ValidationField<T>): ValidatorFn<T> => {
return (item: T): ValidationErrors => {
const itemValue = value(item) as number;
return itemValue > compareValue
? ({ lessThan: { compareValue, actual: itemValue } } as Record<
string,
unknown
>)
: null;
};
};
}
Fluid validations provides an easier way to validate a model in a fluid way. It provides customizations for nested validations for nested properties.
// Sample Types
type ProductAttribute = {
id: string;
productId: string;
};
type ProductImage = {
id: string;
productId: string;
altText: string;
};
type Product = {
id: string;
name: string;
attributes: Nullable<ProductAttribute[]>;
image: ProductImage;
price: number;
email: string;
url: string;
};
const productImageValidator = new FluidValidator<ProductImage>()
.prop('id') // Maps to a particular property in Product Image
.maxLength(36) // Standard Validations listed below
.prop('productId')
.required();
const productAttributeValidator = new FluidValidator<ProductAttribute>()
.prop('id')
.maxLength(36)
.prop('productId')
.required();
const productValidator = new FluidValidator<Product>()
.prop('id')
.maxLength(36)
.prop('name')
.emptyOrWhiteSpace()
.required()
.minLength(1)
.maxLength(50)
.prop('image')
.required()
.custom(({ image }) => {
// custom validations using another validator
return productImageValidator.validate(image);
})
.prop('price')
.greaterThan(0)
.prop('email')
.email()
.prop('url')
.url()
.pattern(/^https:\/\//)
.prop('attributes')
.required()
.custom(({ attributes }) => {
const validationMessages: ValidationErrors = attributes.reduce(
(errors, attribute, index) => {
const validationErrors = productAttributeValidator.validate(attribute);
if (validationErrors) {
errors[index] = validationErrors;
}
return errors;
},
{}
);
return hasKeys(validationMessages) ? validationMessages : null;
});
The prop('propertyName')
method scopes the validations to the property of the model. The propertyName
must be a key of the model being validated.
| Name | Description |
| ----------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
| prop(propName: string): FluidValidator<T>
| Defines the property being validated |
| required(): FluidValidator<T>
| Defines the property is required. |
| maxLength(length: number): FluidValidator<T>
| The max length of a string value |
| minLength(length: number): FluidValidator<T>
| The min length of a string value |
| lessThan(compare: number): FluidValidator<T>
| Compares the property must be less than the compare value |
| greaterThan(compare: number): FluidValidator<T>
| Compres the property must be greater than the compare value |
| emtpyOrWhiteSpace(): FluidValidator<T>
| Checks the value is not empty string or white space. |
| email(): FluidValidator<T>
| Checks the property is an email address |
| url(): FluidValidator<T>
| Checks the property is a url |
| pattern(regEx: RegExp): FluidValidator<T>
| Checks the property is a regular expression |
| custom(func: Func<T | Partial<T>, Nullable<ValidationErrors>>, partial: boolean): FluidValidator<T>
* | Provides the ability to write a custom validation function or reuse another FluidValidator<T>
such as a nested object. |
| validate(item:T): Nullable<Partial<PropertyValidationErrors<T>>>
| Validates and full object for all the properties defined in the validator |
| validatePartial(item: Partial<T>): Nullable<Partial<PropertyValidationErrors<T>>>
| Validates a partial object of the properties provided |
| extends<T & R>(): FluidValidator<T & R>
| Extends the available properties to validate. |
| omit<K extends KeyOfType<T>>(keys: K[]): FluidValidator<Omit<T, K>>
| Omits certain properties from being validated. |
| pick<K extends KeyOfType<T>>(keys: K[]): FluidValidator<Pick<T, K>>
| Picks certain properties to be validated. |
* The custom
function may provide a Partial<T>
so all properties may not be available for validating and you'll want to code if function is a partial check. The assumption is that if you're doing a partial check the object was previously validated in full.
The custom
function accepts a partial
to determine if the validation is a partial validation. This matters when you want to check multiple multiple and dependent properties of the item.