Skip to content

modulify/validator

Repository files navigation

Logo @modulify/validator

codecov Tests Status npm version

This library provides a declarative validation util.

The util does not provide any text messages in the constraints produced and gives only metadata that can be used to create a custom view for them.

Installation

Using yarn:

yarn add @modulify/validator

or, using npm:

npm install @modulify/validator --save

Usage

import {
  Collection,
  Exists,
  Length,
  createValidator,
} from '@modulify/validator'

const validator = createValidator()

const violations = validator.validate({
  form: {
    nickname: '',
    password: '',
  },
}, new Collection({
  form: [
    new Exists(),
    new Collection({
      nickname: new Length({ min: 4 }),
      password: new Length({ min: 6 }),
    }),
  ],
}), /* do not set or set to true for async validation */ false) /* [{
  by: '@modulify/validator/Length',
  value: '',
  path: ['form', 'nickname'],
  reason: 'min',
  meta: 4,
}, {
  by: '@modulify/validator/Length',
  value: '',
  path: ['form', 'password'],
  reason: 'min',
  meta: 6,
}] */

Constraints

Constraints provide information of how the value should be validated.

Available from the box:

  • Collection – used for validating objects' structure;
  • Each – used for validating arrays' elements; applies specified constraints to each element of an array;
  • Exists – used for checking if a value is defined; useful for finding missing keys;
  • Length – used for checking arrays' and string's length, available settings (all optional) are:
    • exactnumber, array or string should have exactly specified count of elements or characters;
    • maxnumber, maximum elements in array or maximum characters in string;
    • minnumber, minimum elements in array or minimum characters in string;
  • OneOf – used for restricting which values can be used.

There is no any basic constraint class to extend, but they should follow signature described in types/index.d.tsConstraint.

Validators

Validators provide validation logic that relies on information provided by constraints.

There is no any basic validator class to extend, but they should follow signature described in types/index.d.tsConstraintValidator.

Provider

Provider is used to bind constraints with their validators, provides a validator for a constraint.

All providers should follow signature described in types/index.d.tsProvider.

This feature is responsible for extending validation capabilities. Custom provider can be passed into createValidator function or override method of Validator instance.

There is a built-in provider – ProviderChain. It allows to "chain" providers – if suitable validator was not found in currently used provider, it will try to find it in previous provider that was overridden by override method.

import type {
  Constraint,
  ConstraintValidator,
  ConstraintViolation,
  Key,
  Provider,
} from '@modulify/validator'

import {
  ProviderChain,
  createValidator,
} from '@modulify/validator'

class Email implements Constraint {
  public readonly name = '@app/validator/Email'

  toViolation (value: unknown, path: Key[]): ConstraintViolation {
    return {
      by: this.name,
      value,
      path,
    }
  }
}

class EmailValidator implements ConstraintValidator {
  private readonly _constraint: Email

  constructor (constraint: Email) {
    this._constraint = constraint
  }

  validate (value: unknown, path?: Key[]): ConstraintViolation | null {
    if (!(typeof value === 'string') || !/\S+@\S+\.\S+/.test(value)) {
      return this._constraint.toViolation(value, path)
    }

    return null
  }
}

then

const provider = new ProviderChain(new class implements Provider {
  get (constraint: Constraint) {
    return constraint instanceof Email ? new EmailValidator(constraint) : null
  }

  override (provider: Provider): Provider {
    return new ProviderChain(provider, this)
  }
})

or

const provider = new class implements Provider {
  get (constraint: Constraint) {
    return constraint instanceof Email ? new EmailValidator(constraint) : null
  }

  override (provider: Provider): Provider {
    return new ProviderChain(provider, this)
  }
}

and then

const validator = createValidator(provider)

or

const validator = createValidator()
const overridden = validator.override(provider) // it creates new validator instance, so validator !== overridden