@smayas/validator

An easy to use, promise based constraint based constraint validation package inspired by the Symfony framework

Usage no npm install needed!

<script type="module">
  import smayasValidator from 'https://cdn.skypack.dev/@smayas/validator';
</script>

README

Validator

An easy to use, promise based constraint based constraint validation package inspired by the Symfony framework

Installation

Install the package with npm install @smayas/validator

Usage

Basic value validation

import { Validator, ConstraintViolationInterface } from "@smayas/validator";
import * as Constraint from "@smayas/validator/dist/constraints";

const validator = new Validator();

// validating an e-mail value
const value = 'some@invalid';
const constraint = new Constraint.Email({
    message: 'This e-mail "%{value}" does not seem to be valid'
});

const promise = validator.validate(value, constraint);

promise.then(function(violations: ConstraintViolationInterface[]) {
    if(violations.length > 0) {
        return console.log(violations[0].message);
    }

    return console.log(`The e-mail ${value} is a valid e-mail address`);
});

Object graph validation

In addition to simple value validation, object graphs can be validated as well.

import { Validator, ConstraintViolationInterface } from "@smayas/validator";
import * as Constraint from "@smayas/validator/dist/constraints";

// creating the validator instance
const validator = new Validator();

// an imaginary user object
const user = {
    name: 'Bruce Wayne',
    email: 'bruce.wayne@wayneenterprises.com',
    age: 55
}

// declaring the validation schema
const validationSchema = {
  name: [
      new Constraint.NotBlank({
          message: "The user's name cannot be blank"
      })
  ],
  email: [
      new Constraint.NotBlank({
          message: "The e-mail address cannot be blank"
      }),
      new Constraint.Email({
          message: "The e-mail address does not seem to be valid"
      })
  ],
  age: [
      new Constraint.NotBlank({
          message: "User's age cannot be blank",
      }),
      new Constraint.Range({
            min: 20,
            max: 50,
            minMessage: "The user must be between 25 and 50 years of age"
      })
  ]
};

// validating the object
const promise = validator.validate(user, validationSchema);

// handling violations if present
promise.then(function(violations: ConstraintViolationInterface[]) {
    if(violations.length > 0) {
        return console.log(violations);
    }

    console.log("User is valid");
});

Validation groups

Validation groups are useful when you want to validate only certain properties of an object and not the entire object. By design, all constraint validation groups default to a global 'Default' one.

import { Validator, ConstraintViolationInterface } from "@smayas/validator";
import * as Constraint from "@smayas/validator/dist/constraints";

const validator = new Validator();

const user = {
    email: 'bruce.wayne@wayneenterprises.com',
    age: ''
}

const validationSchema = {
  email: [
      new Constraint.NotBlank({
          message: "User's e-mail cannot be blank",
          groups: ['Default', 'email']
      }),
      new Constraint.NotBlank({
          message: "Invalid user e-mail provided",
          groups: ['Default', 'email']
      }),
  ],
  age: [
      new Constraint.NotBlank({
          message: "User's age cannot be blank",
      })
  ]
};

const promise = validator.validate(user, validationSchema, ['email']);

promise.then(function(violations: ConstraintViolationInterface[]) {
    if(violations.length > 0) {
        return console.log(violations);
    }

    console.log("User is valid");
});

Array validation

A specific constraint called All is in charge of validating each element of a provided array

import { Validator, ConstraintViolationInterface } from "@smayas/validator";
import * as Constraint from "@smayas/validator/dist/constraints";

const validator = new Validator();

const tags = ['music', 'business', false, 'entertainment']

const constraint = new Constraint.All({
    constraints: [new Constraint.NotBlank({
        message: 'Element at index %{_index} is blank'
    })]
});
// passing only the 'email' group will only validate constraints with the e-mail group assigned to them
const promise = validator.validate(tags, constraint);

promise.then(function(violations: ConstraintViolationInterface[]) {
    if(violations.length > 0) {
        // Will contain a violation with the message set to "Element at index 2 is blank"
        return console.log(violations);
    }

    console.log("All tags are valid");
});

Constraints

Out of the box, the following validation constraints are available

NotBlank

Validates that a value is not blank. Meaning not equal to a blank string, false, null or undefined.

Options Type Description
message string The validation message to display when the constraint violation fails.
Default to "This value cannot be blank"

Range

Validates that a value is between a minimum and maximum value

Options Type Description
min number This required option is the “min” value. Validation will fail if the given value is less than this min value.
max number This required option is the “max” value. Validation will fail if the given value is mor than this max value.
minMessage string The message to display if the min validation fails.
Defaults to "This value should be %{min} or more."
maxMessage string The message to display if max min validation fails.
Defaults to "This value should be %{max} or more."

Length

Validates that a string length is between a minimum and maximum value

Options Type Description
min number This required option is the “min” value. Validation will fail if the given value's length is less than this min value.
max number This required option is the “max” value. Validation will fail if the given value's length is over this max value.
minMessage string The message to display if the min validation fails.
Defaults to "This value should be %{min} characters in length or more."
maxMessage string The message to display if max validation fails.
Defaults to "This value should be %{max} characters in length or less."

Choice

Validates that a value is in a specific choice list

Options Type Description
choices array An array of valid choices. Validation will fail if the given value is not in this array of choices
message string The message to display if the value is not in the given array.
Defaults to "This value is not supported."

Count

Validates that an array has a specific number of elements present

Options Type Description
min number This required option is the “min” value. Validation will fail if the given array's length is less than this min value.
max number This required option is the “max” value. Validation will fail if the given array's length is over this max value.
minMessage string The message to display if the min validation fails.
Defaults to "This collection should have %{min} characters in length or more."
maxMessage string The message to display if max validation fails.
Defaults to "This collection should have %{max} characters in length or less."

Regex

Validates that a string matches a given format

Options Type Description
pattern RegExp The pattern to validate against
match boolean Whether the validation constraint should evaluate as true or false.
Defaults to true
message string The message to display if the value is not in the correct format.
Defaults to "This value is not in a valid format."

Custom constraints

Although the default constraints are enough in most cases, sometimes you may need to create your own validation and this is achieved using a custom callback constraint

Let's imagine the following scenario where only a Mercedes car is allowed as a value and BMW is disallowed

import { Validator, ConstraintViolationInterface } from "@smayas/validator";
import * as Constraint from "@smayas/validator/dist/constraints";


// callback wil be invoked during validation
const callback = async (value: any, constraint: ConstraintInterface, context: ExecutionContextInterface): Promise < ConstraintViolationInterface[] > => {
  const violations = [];

  if (value === 'BMW') {
    const customViolationErrorCode      = 'callback-constraint.invalid-car.error';
    const customViolationErrorMessage   = 'Only %{allowed_brand} cars are allowed but you provided %{disallowed_brand}';
    const customViolationErrorMessageParameters = {
        'allowed_brand': 'Mercedes',
        'disallowed_brand': value
    };

    const violation = context.createViolation(constraint, value, value, customViolationErrorCode, customViolationErrorMessage, customViolationErrorMessageParameters);
    violations.push(violation);
  }

  return violations;
};

const validator = new Validator();

const promise = validator.validate('BMW', [
    new Constraint.NotBlank({
        message: 'A valid car brand must be provided'
    }),
    new Constraint.Callback({
        callback: callback
    })
]);

promise.then(function(violations: ConstraintViolationInterface[]) {
    console.log(violations);
});

The violatiotion message above will be

Only Mercedes cars are allowed but you provided BMW