@ngserveio/you-good
TypeScript icon, indicating that this package has built-in type declarations

8.1.1 • Public • Published

@ngserveio/you-good

A client and backend agnostic validation library created by NgServe.io.

Read More at Angular Tutorial - Sharing Validations Between Angular and NestJs

Running unit tests

Run nx test shared-validator to execute the unit tests via Jest.

Purpose

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.

Types

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

Validators

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

Validating

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.

How to Use validate

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 } }

Sample Validators

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;
    };
  };
}

FluidValidator

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.

Methods

| 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.

Package Sidebar

Install

npm i @ngserveio/you-good

Weekly Downloads

47

Version

8.1.1

License

MIT

Unpacked Size

49.7 kB

Total Files

11

Last publish

Collaborators

  • ngserve