Building Validations

Setting up validations is really easy with v8n. Everything starts from the base function v8n(). After calling this function you can chain all the rules you like to create simple or complex validations.

v8n(); // Chain after this!

Chaining rules

In order to build the validation we use a paradigm called chaining. This essentially means that you build each validation rule after one another. This creates a fluent writing experience. You can chain any rules to create your custom validations.

v8n()
  .string()
  .first("B")
  .test("Bread"); // The validation strategy, this will be true

WARNING

There are no sanity checks, combining conflicting rules is possible.

At the end of your rules you will chain a validation strategy. This will end the chain and give you the result of the validation. There are multiple available, make sure to find the right one for you.

There is no need to write one rule per line, it's just a nice convention to follow for clean code. For simple validations you can simply inline your validation. You can also use these in conditionals and loops directly.

if (v8n().number().test(2)) {
  // If 2 is a number, do something!
}

It's important to keep types in mind when validating. A string that consists of just a number is not a number in the JavaScript sense. If you pass '12' to the number() rule you'll get a negative result since '12' is a string. v8n makes no assumptions about your types, so you need be explicit here and convert the string manually beforehand.

v8n()
  .number()
  .test("12"); // False because it's a string

v8n()
  .number()
  .test(Number("12")); // True because Number turns '12' into 12

Some rules also have parameters to control how they validate your value. These are passed right in the chain. Notice that the stuff you pass to rules is not validated, it is simply configuration for the rule.

v8n()
  .greaterThan(2)
  .test(1); // False, 1 is not at least 2

For all the rules you can chain take a look at the full list.

Optional Validations

If you have an optional value of some sort you might want to verify that it passes some sort of validation only if it's defined. You could wrap the whole validation in an if clause, but really there's a much better way. v8n provides optional validation out of the box. This means you can just write your rule-chain as you like and wrap it in an optional() validation rule.

const specialString = v8n()
  .string()
  .length(12);

v8n()
  .optional(specialString)
  .test("I like apples"); // true

v8n()
  .optional(specialString)
  .test("I like oranges"); // false

v8n()
  .optional(specialString)
  .test(undefined); // true

Modifiers

Sometimes you might want the inverse of a rule somewhere in your validation. For example you might want to check that something isn't null when it can be any other type. This is where modifiers come in. Modifiers are similar to rules, they are also chained but instead of validating anything they will augment the following rule.

v8n()
  .not.null()
  .test(2); // True, 2 isn't null

v8n()
  .every.number()
  .test([1, 2, "hello"]); // False, hello isn't a number

v8n()
  .some.number()
  .test([1, 2, "hello"]); // True, there is at least one number

TIP

Modifiers can be combined on a rule. Simply chain them!

Notice how modifiers don't have braces (), this distinguishes them from regular rules. There are three modifiers provided which you can use. Two of them cater to arrays and one is more general purpose. Take a peek at all the built-in modifiers for a little more info.

Validation strategies

Most validation will only return true or false. While v8n offers this, it also gives you the ability to validate with different output formats. There are currently 4 validation strategies for use and all of them are slightly different from one another.

The validation strategy is chosen with the method at the end of your validation chain. This method also receives the value you want to validate as it's only argument. So keep in mind that whatever you actually want to validate is written at the end of the chain, not anywhere within the rules or at the beginning.

Boolean based validation

Of course when validating many people will want to know if the given value passes or not. This is regular boolean validation and v8n performs this if you use the test() method. Quite simply this will return true if all the rules pass and false if any of them fails.

v8n()
  .string()
  .test("Melon"); // True

v8n()
  .string()
  .test({ age: 3 }); // False, this is an object

This strategy is most useful for conditionals since you can pass its result directly into an if statement or similar. For more details about test() and for additional examples take a look at it's API documentation.

Array based validation

If you really need to know what was wrong with the value you passed to v8n, simply getting true or false won't do you any good. This is where array-based comes in. If you use testAll() you will always receive an array in return. The array will be empty if no rules failed, but it will contain ValidationError objects indicating each fail if they occur. This also means that this strategy does not stop when any rule fails like the boolean-based version does, instead it will always run all the rules.

v8n()
  .string()
  .first("H")
  .last("o")
  .testAll("Hello"); // Returns [] since every rule passes

v8n()
  .string()
  .first("H")
  .last("o")
  .testAll("Hi"); // Returns [ValidationError{rule: {name: "last"...}, ...}]

This is useful for providing detailed error messages but can also be used for any number of other purposes. For some more in-depth examples head over to the documentation for testAll().

TIP

The array will contain ValidationError objects that you can work with.

WARNING

Keep in mind that this validation strategy will work exactly opposite if used in conditionals compared to test(). This is because JavaScript will consider an empty array to be a falsy value, so no errors would actually result in the conditional passing here. Make sure to keep this in mind when checking whether the validation passed here.

Exception based validation

In some cases you might not want to explicity check if validation passed in a process. If your entire setup is based on a try-catch you might run all of your code and catch any errors with a single catch to simplify your code. v8n offers this type of validation too. When you use check() an exception will be thrown if any rule fails. If all rules pass there just won't be any return value at all.

try {
  v8n()
    .boolean()
    .check("Lion"); // Will throw an exception since "Lion" is not a boolean
} catch (exception) {
  // Handle exception in some way
  exception.rule; // Rule object of the first failed rule
}

The resulting ValidationError will also contain information about the rule that failed so that you may display errors or similar. You can find out more in the documentation for check().

Asynchronous validation

A very interesting use-case for validation might be an availability check for a username or an email. Since your frontend can't know if a user is in the database, you would need to query the server for this. Requesting something from the server takes time and any library you might use to achieve it will perform the request in an asynchronous way. All the strategies above don't support this type of rule though, since they will return a result before the server has a chance to respond. This is where we need testAsync() to run validations asynchronously and await all the returns. You will ultimately receive a Promise back which you can react to. It resolves to the value your validated and would reject to a ValidationError.

v8n()
  .string() // You can use regular rules too
  .checkUsernameAvailable() // This asks something from a server
  .testAsync("myUsername") // Promise return
  .then(validatedValue => validatedValue)
  .catch(exception => exception);

WARNING

There are no built-in asynchronous rules. If you want to use this feature, you can check out the guide on Extending and specifically on the creation of asynchronous rules.

Make sure to take a look the documentation for testAsync() to get a good grasp of the return values it will yield.

Reusing validations

The flexibility of chaining adds a very nice additional feature. If you have multiple validations that share common rules you can reuse them across your file or even export them for use throughout the whole project. This is particulary useful for complex validations. This also allows you to validate different values with the same set of rules.

const val = v8n()
  .not.null()
  .greaterThan(2);

val.test(3); // True
val.test(null); // False
val.lessThan(5).test(7); // False

You can of course perform different validation strategies on the same set of rules. The rule-chain is completely decoupled from anything that comes after it, so you can do as much as you like with it after creation.

Last Updated: 7/1/2022, 2:18:16 AM