Validasaur

tag CI License: MIT 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"
  }
}

Make Your own Simple Rule Validation

In this example we will make a isOdd rule validation that check odd number.

First, let’s make is_odd.ts like this:

import { invalid, Validity } from "https://deno.land/x/validasaur/src/mod.ts";

export function isOdd(value: any): Validity {
  if (typeof value !== "number") {
    return invalid("isOdd", { value });
  }

  if (value % 2 !== 1) {
    return invalid("isOdd", { value });
  }
}

Now, we can use it like this:

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";
import { isOdd } from "./is_odd.ts";

const inputs = {
  number: 20
};

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

console.log({ passes, errors });

Make More Advanced Rule Validation

In this example we will make a unique rule that check value availability in the database. This rule accepts table and column as arguments, then calling database function to check availability based on those arguments.

First, let’s make our unique.ts:

import db from "./your_db_service.ts";
import { invalid, Validity, Rule } from "https://deno.land/x/validasaur/src/mod.ts";

export function unique(table: string, column: string): Rule {
  return async function uniqueRule(value: any): Promise<Validity> {
    if (typeof value !== "string" || typeof value !== "number") {
      return invalid("unique", { value, table, column });
    }

    const data = await db.findOne(table, { [column]: value });
    if (data) {
      return invalid("unique", { value, table, column });
    }
  };
}

Now we can use it like this:

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

const inputs = {
  email: "emsifa@gmail.com"
};

const [ passes, errors ] = await validate(inputs, {
  email: [required, isEmail, unique("users", "email")]
});

console.log({ passes, errors });

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.