Validasaur

tag CI License: MIT tag

Validasaur is Deno validation library slightly inspired by Laravel Validation.

Examples

Basic Usage

Write your example.ts like this:

import { validate } from "https://deno.land/x/validasaur/src/mod.ts";
import { required, isNumber } from "https://deno.land/x/validasaur/src/rules.ts";

const inputs = {
  name: "",
  age: "20"
};

const [ passes, errors ] = await validate(inputs, {
  name: required,
  age: [required, isNumber]
});

console.log({ passes, errors });

Run code above with:

deno run example.ts

And this is the result:

{
  "passes": false,
  "errors": {
    "name": {
      "required": "name is required"
    },
    "age": {
      "isNumber": "age must be a number"
    }
  }
}

Formatting Errors

If you want a simpler error message, you can use flattenMessages or firstMessages to format error messages.

For example:

import { validate, flattenMessages, firstMessages } from "https://deno.land/x/validasaur/src/mod.ts";
import { required, isNumber } from "https://deno.land/x/validasaur/src/rules.ts";

const inputs = {
  name: "",
  age: "20"
};

const [ passes, errors ] = await validate(inputs, {
  name: required,
  age: [required, isNumber]
});

const firstErrors = firstMessages(errors);
const flattenErrors = flattenMessages(errors);

// Show the difference
console.log({
  defaultErrors: errors,
  firstErrors,
  flattenErrors
});

Result:

{
  "defaultErrors": {
    "name": {
      "required": "name is required"
    },
    "age": {
      "isNumber": "age must be a number"
    }
  },
  "firstErrors": {
    "name": "name is required",
    "age": "age must be a number"
  },
  "flattenErrors": {
    "name.required": "name is required",
    "age.isNumber": "age must be a number",
    "name": "name is required",
    "age": "age must be a number"
  }
}

Custom Error Message

import { validate, flattenMessages } from "https://deno.land/x/validasaur/src/mod.ts";
import {
  required,
  isNumber,
  isString,
  validateArray,
  validateObject
} from "https://deno.land/x/validasaur/src/rules.ts";

const inputs = {
  name: "",
  age: '12',
};

const [passes, errors] = await validate(inputs, {
  name: required,
  age: [required, isNumber],
}, {
  messages: {
    "name": "Nama tidak boleh kosong",
    "age.required": "Usia tidak boleh kosong",
    "age.isNumber": "Usia harus berupa angka",
    // Use this if you want same message for any rule fail
    // "age": "Usia tidak valid"
  },
});

console.log({ passes, errors });

Result:

{
  "passes": false,
  "errors": {
    "name": {
      "required": "Nama tidak boleh kosong"
    },
    "age": {
      "isNumber": "Usia harus berupa angka"
    }
  }
}

Validating Array and Object

import { validate, flattenMessages } from "https://deno.land/x/validasaur/src/mod.ts";
import {
  required,
  isNumber,
  isString,
  validateArray,
  validateObject
} from "https://deno.land/x/validasaur/src/rules.ts";

const inputs = {
  name: "",
  age: "20",
  skills: ["PHP", "Node.js", 0, "Deno"],
  address: {
    street: null,
    city: "Jakarta",
    country: "Indonesia",
  }
};

const [ passes, errors ] = await validate(inputs, {
  name: required,
  age: [required, isNumber],

  // validateArray(required: boolean, rules: Rule[])
  skills: validateArray(true, [isString]),

  // validateObject(required: boolean, rules: ValidationRule)
  address: validateObject(true, {
    street: required,
    city: required,
    country: required,
  }),
});

const flattenErrors = flattenMessages(errors);

console.log({ passes, flattenErrors });

Result:

{
  "passes": false,
  "flattenErrors": {
    "name.required": "name is required",
    "age.isNumber": "age must be a number",
    "skills.2.isString": "2 must be a string",
    "address.street.required": "street is required",
    "name": "name is required",
    "age": "age must be a number",
    "skills.2": "2 must be a string",
    "address.street": "street is required"
  }
}

Available Rules

required

Value under this field should not be null, undefined, or an empty string ("").

  • Invalid values: null, undefined, ""
  • Valid values: "0", [], {}, 0, etc.

fileExists(pathPrefix: string)

Value under this field must be existed file.

For example you have file /var/www/media/image.jpg in your file system:

const [ passes, errors ] = await validate({
  file1: "image.jpg",
  file2: "image.jpg",
  file3: "not-image.txt"
}, {
  file1: fileExists("/var/www/media"), // << this will be fail because it check "/var/www/mediaimage.jpg"
  file2: fileExists("/var/www/media/"), // << this will be pass
  file3: fileExists("/var/www/media/"), // << this will be fail because it check "/var/www/media/not-image.txt" that is not exists
})

isArray

Value under this field must be an array.

  • Invalid values: "", 10, 0.5, etc.
  • Valid values: [], [1, 2, 3], [{x: 10}, {x: 12}], etc.

isBool

Value under this field must be a boolean.

  • Invalid values: "", 10, 0.5, etc.
  • Valid values: true and false.

isEmail

Value under this field must be valid email address.

  • Invalid values: "someone name", 123, foo.bar.baz, foo@bar@baz, etc.
  • Valid values: "someone@mail.com", "someone@mail.co.id", "someone@[1.2.3.4]", etc.

isFloat

Value under this field must be a float number.

  • Invalid values: "0.1", [], 0, 1, 123, etc.
  • Valid values: 0.1, 1.2, 12.345, etc.

isIn(allowedValues: PrimitiveTypes[])

Value under this field must be one of allowed values.

Example:

const [ passes, errors ] = await validate({
  value1: "yes",
  value2: "no",
  value3: "maybe"
}, {
  value1: isIn(["yes", "no"]), // passes
  value2: isIn(["yes", "no"]), // passes
  value3: isIn(["yes", "no"]), // fail
})

isInt

Value under this field must be an integer.

  • Invalid values: 0.5, "123", etc.
  • Valid values: 0, 123, etc.

isNumber

Value under this field must be a float or an integer.

  • Invalid values: "1", "1.5", etc.
  • Valid values: 1, 1.5, etc.

isNumeric

Same as asNumber, but it allows numeric string.

  • Invalid values: "1.0abc", "x.1", etc.
  • Valid values: 1, 1.5, "2", "2.5", etc.

isString

Value under this field must be a string.

  • Invalid values: 1, 1.5, etc.
  • Valid values: "1", "1.5", "foo", etc.

lengthBetween(minLength: number, maxLength: number)

Value under this field must be a string that has char length between minLength and maxLength.

Example:

const [ passes, errors ] = await validate({
  value1: 'foo',
  value2: 'foobar',
  value3: 'fo',
  value4: 'foobars',
}, {
  value1: lengthBetween(3, 6), // passes
  value2: lengthBetween(3, 6), // passes
  value3: lengthBetween(3, 6), // fail
  value4: lengthBetween(3, 6), // fail
})

match(regex: RegExp, trim: boolean = false)

Value under this field must be a string that match with given regex.

const [ passes, errors ] = await validate({
  value1: 'foo$',
  value2: '$foo',
  value3: 'foo1',
  value4: 'foo2',
  value5: ' foo3',
  value6: ' foo4',
}, {
  value1: match(/^[a-z0-9]{4}$/), // fail
  value2: match(/^[a-z0-9]{4}$/), // fail
  value3: match(/^[a-z0-9]{4}$/), // passes
  value4: match(/^[a-z0-9]{4}$/), // passes
  value5: match(/^[a-z0-9]{4}$/), // fail
  value6: match(/^[a-z0-9]{4}$/, true), // passes after trim
})

maxLength(minValue: number)

Value under this field must be a string that has char length lower or equals maxValue.

Example:

const [ passes, errors ] = await validate({
  value1: 'foobarbaz',
  value2: 'foobar',
}, {
  value1: maxLength(6), // fail
  value2: maxLength(6), // passes
})

maxNumber(maxValue: number)

Value under this field should be a number that is not higher than maxValue.

Example:

const [ passes, errors ] = await validate({
  value1: 6,
  value2: 5.01,
  value3: 5,
  value4: 4
}, {
  value1: maxNumber(5), // fail
  value2: maxNumber(5), // fail
  value3: maxNumber(5), // passes
  value4: maxNumber(5), // passes
})

minLength(minValue: number)

Value under this field must be a string that has char length higher or equals minValue.

Example:

const [ passes, errors ] = await validate({
  value1: 'foo',
  value2: 'foobar',
}, {
  value1: minLength(6), // fail
  value2: minLength(6), // passes
})

minNumber(minValue: number)

Value under this field should be a number that is not lower than minValue.

Example:

const [ passes, errors ] = await validate({
  value1: 1,
  value2: 4.99,
  value3: 5,
  value4: 5.01,
}, {
  value1: minNumber(5), // fail
  value2: minNumber(5), // fail
  value3: minNumber(5), // passes
  value4: minNumber(5), // passes
})

notIn(disallowedValues: PrimitiveTypes[])

Value under this field must not be one of disallowed values.

Example:

const [ passes, errors ] = await validate({
  value1: "yes",
  value2: "no",
  value3: "maybe"
}, {
  value1: notIn(["yes", "no"]), // fail
  value2: notIn(["yes", "no"]), // fail
  value3: notIn(["yes", "no"]), // passes
})

notNull

Value under this field must not be null.

numberBetween(minValue: number, maxValue: number)

Value under this field must be a number between minValue and maxValue.

const [ passes, errors ] = await validate({
  value1: 5,
  value2: 10,
  value3: 4.99,
  value4: 10.01,
}, {
  value1: numberBetween(5, 10), // passes
  value2: numberBetween(5, 10), // passes
  value3: numberBetween(5, 10), // fail
  value4: numberBetween(5, 10), // fail
})

TODOS

  • isUrl rule.
  • isIpAddress rule.
  • isIpv4 rule.
  • isIpv6 rule.
  • startsWith rule to check string prefix.
  • endsWith rule to check string postfix.
  • add more examples to documentation.