Validator is a highly optimized data validation library designed for simplicity, performance, and ease of use. It aims to meet the needs of developers who require minimal blocking time and low CPU/memory overhead.
Focusing solely on data validation allows us to greatly optimize this library.
I advocate for tools that excel in a single task.
I believe that, in most cases, we should validate data before performing any transformation.
This approach simplifies and maintains a clear mental model of what data validation is and how it should be implemented in our applications.
JSON data types do not require asynchronous validation.
Avoid introducing side effects within validations.
Don't do this:
// definition
const validate = and([
string,
minLength(5),
async (v) => {
if(!(await File.exists(v))) {
throw Error("File does not exist");
}
}
]);
// endpoint
await assert(validate, filename);
Do this instead:
// definition
const validate = and([
string,
minLength(5),
]);
// endpoint
assert(validate, filename);
if(!(await File.exists(filename))) {
throw Error("File does not exist");
}
Compilation with Function
/eval
syntax is not allowed in all environments and, more importantly, it would mean maintaining two different runtime implementations, which I do not want.
It also sacrifices initial blocking for faster subsequent runs, which might be useful in some scenarios. However, this library is primarily designed for serverless runtimes, where this would result in drastically slower performance.
extend
/omit
In order to allow such methods, we would have to make the schema accessible from the outside.
This would change the design from using individual callable validations to using objects with properties, one of which is the validation.
Additionally, this would make it possible, for example, to extend any object, even if we don't want users to have such capability.
To address this issue, we would need to introduce some form of object schema freezing.
All of this complicates the API, slows down the library, and increases the bundle size.
You can make an object extendable by exporting its schema separately and then spreading it inside another schema.
map
/set
The main focus of this library is the data validation of JSON (primarily from fetch requests).
JSON does not support these data types, so it makes no sense to include them in this library.
If you want to use this library with these higher-level primitives, then I recommend validating the input of these primitives.
any
/unknown
You should always define concrete types.
Otherwise, what's the point of using TypeScript together with this library?
null
/undefined
Checking strictly for null
or undefined
alone makes no sense.
You always want to know if something can be something or nothing.
Therefore, you should always use nullable
, optional
, or nullish
instead.
Validator is engineered to deliver exceptional performance:
assert(string, "Hello, World!");
assert(number, 420);
assert(boolean, true);
value(26);
notValue(0);
minValue(18);
maxValue(100);
rangeValue(18, 100);
length(5);
notLength(0);
minLength(8);
maxLength(16);
rangeLength(8, 16);
const register = object({
email: and([string, rangeLength(5, 35), email]),
password: and([string, rangeLength(8, 16)]),
role: union(["ADMIN", "USER"]),
friends: array(string)
});
assert(
register,
{
email: "yamiteru@icloud.com",
password: "Test123456",
role: "ADMIN",
friends: ["Joe"]
}
);
yarn add @the-minimal/validator