An extensible object validation library.
For further insights, read on.
To install this library use your favorite package manager. No additional steps are required to start using the library.
npm install @n7e/validation
This library is implemented in TypeScript but can be used with JavaScript without any additional steps.
The protagonist of this library is the validator. Validators lets you validate
arbitrary objects against a set of provided rules. All validators implement the
Validator
interface. To reference it simply import it and use it as a type.
import { Validator } from "@n7e/validation";
function doSomethingWith(validator: Validator): void {
// ...
}
Validator
is just an interface describing the functionality of a validator.
To create validators use a validator factory or
validator builder.
To validate an object use the validate
method:
const rules = {
key: "present"
};
validator.validate({key: "value"}, rules);
The rules follow a defined format.
If the object is valid according to all the provided rules nothing happens.
However, if one or more rules are broken an Issues
error is thrown. Depending
on the validation strategy being used the error contains
all or some of the broken rules with a short description of each issue.
To make things easy the Issues
error provides an easy-to-read format when
serialized to JSON:
{
"key": [
"Must be present"
]
}
When validating an object one or more properties may break the given rules.
Whether the validator continues to check for additional issues and issues on
additional properties is defined by a ValidationStrategy
.
A validation strategy can be passed to the validate()
method when invoked. You
can also configure a validators default validation strategy using a
validator builder.
The available validation strategies are:
ValidationStrategy.STOP_AT_FIRST_INVALID_RULE
- Throws an exception at the first failed rule.
ValidationStrategy.STOP_AT_FIRST_INVALID_PROPERTY
- Validates all rules for a property before throwing an exception if any of them failed.
ValidationStrategy.RUN_ALL_RULES
- Runs all rules for all properties and throws an exception if any of them failed.
To create a pre-configured validator instance you should use a
ValidatorFactory
.
import type { ValidatorFactory } from "@n7e/validation";
function someFunction(validatorFactory: ValidatorFactory): void {
const validator = validatorFactory.createValidator();
// ...
}
ValidatorFactory
is just an interface describing the functionality of a
validation factory. To create a validator factory instance you need to reference
a specific implementation.
There's a provided default validator factory that creates a validator instance with all available rules.
import { DefaultValidatorFactory } from "@n7e/validation";
const validator = new DefaultValidatorFactory().createValidator();
To configure and build a custom validator instance you should use a
ValidatorBuilder
.
import { ValidationStrategy } from "@n7e/validation";
validatorBuilder
.withRule(new SomeCustomRule())
.withRule("custom-rule", (value, parameters) => {})
.withDefaultStrategy(ValidationStrategy.STOP_AT_FIRST_INVALID_RULE);
A validator builder can be passed around and incrementally augmented to eventually produce a desired validator.
const validator = validatorBuilder.build();
ValidatorBuilder
is just an interface describing the functionality of a
validation builder. To create a validator builder instance you need to reference
a specific implementation.
There's a provided implementation of ValidatorBuilder
ready to use.
import { DefaultValidatorBuilder } from "@n7e/validation";
const validatorBuilder = new DefaultValidatorBuilder();
// Add any desired rules and other configuration...
const validator = validatorBuilder.build();
A rule is a single property criteria that must be adhered to. The simplest
example is the present
rule that ensures that the property exists on the
object being validated.
To define the rules a property needs to adhere to a defined format needs to be followed:
"type:string,array"
^ ^
| |
Rule identifier |
|
Optional parameters
Any whitespace around rule parameters is trimmed.
Rules are separated by a single |
character. Here are some examples:
- Single rule without parameters
"present"
- Multiple rules without parameters
"present|numeric"
- Single rule with a parameter
"type:string"
- Single rule with multiple parameters
"type:string,array,date"
- Multiple rules with parameters
"present|type:string,array|min:5"
There's a provided set of available validation rules. Validators created with the default validator factory will include all available validation rules.
Verifies that a property is present.
// This will fail.
validator.validate({}, {key: "present"});
If a property is absent only the present
rule will be checked so this will
pass:
validator.validate({}, {key: "numeric"});
Verifies that values are of a certain type.
// This will pass.
validator.validate({key: "value"}, {key: "type:string"});
The type can be any of the possible JavaScript typeof values:
- string
- number
- object
- function
- undefined
- boolean
- symbol
- bigint
Or any from the extended list below:
- array
- date
- null
Pass multiple types to verify that the value is one of many possible types:
// This will pass.
validator.validate({key: "value"}, {key: "type:array,boolean,string"});
Verifies that values are numeric.
// This will pass.
validator.validate({key: "5"}, {key: "numeric"});
Verifies that values are at least a given value.
// This will pass.
validator.validate({key: 5}, {key: "min:5"});
If the value is a string, a collection (array, set, map etc. basically any
object with a length
or size
property) or an iterator the size of the value
will be compared to the minimum value. In any other case the value will be
compared as is.
// This will fail since the length of the string is 1.
validator.validate({key: "7"}, {key: "min:5"});
Verifies that values are at most a given value.
// This will pass.
validator.validate({key: 5}, {key: "max:5"});
If the value is a string, a collection (array, set, map etc. basically any
object with a length
or size
property) or an iterator the size of the value
will be compared to the minimum value. In any other case the value will be
compared as is.
// This will pass since the length of the string is 1.
validator.validate({key: "7"}, {key: "max:5"});
Verifies that values are UUIDs.
// This will pass.
validator.validate({key: "00000000-0000-0000-0000-000000000000"}, {key: "uuid"});
This rule accepts nil UUIDs.
You can easily add your own rules and use them using the defined format. To do so either create a rule class or a rule function and register it using a validator builder.
Create a class that implements the Rule
interface (This will work with
JavaScript as well, just omit the types):
class CustomRule implements Rule {
public readonly identifier = "custom-rule";
public check(value: unknown, parameters: Array<string>): void {
// If the value is invalid throw an Issue error with a short description
// of the issue, otherwise do nothing.
}
}
To make it available to the validator register the rule using a validator builder:
const validator = validatorBuilder
.withRule(new CustomRule())
.build();
validator.validate({}, {key: "custom-rule"});
Provide an identifier and a function to a validator builder to make the rule available to the validator:
const validator = validatorBuilder
.withRule("custom-rule", (value, parameters) => {
// If the value is invalid throw an Issue error with a short description
// of the issue, otherwise do nothing.
})
.build();
validator.validate({}, {key: "custom-rule"});