When it comes to user input validation, particularly when designing (RESTful, GraphQL) APIs, it's important to have these objectives in mind:
- Validator needs to support various data types such as emails, mobile phone numbers in different locales, credit cards, etc. Well tested and already used in production by many companies.
- It needs to have an API (interface) that allows writing validation rules with the minimum amount of boilerplate code.
- This interface needs to be strongly typed, allowing a better development experience (DX) when used with TypeScript.
I think validator.js (β17k) fits perfectly into the first requirement. And, in order to make it less verbose, I would suggest using the following API (interface):
import { validate } from "validator-fluent";
const input = {
givenName: "John",
familyName: "Doe",
email: "john@example.com",
age: "18",
website: "",
};
const [data, errors] = validate(input, (value) => ({
given_name: value("givenName")
.notEmpty()
.isLength({ min: 3, max: 25 }),
family_name: value("familyName")
.notEmpty()
.isLength({ min: 1, max: 25 }),
email: value("email")
.notEmpty()
.isEmail(),
age: value("aga").toNumber(),
website: value("website").isURL(),
});
if (Object.keys(errors).length === 0) {
await db.table("customer").insert(data);
}
Validation Only (mode)
Often, it also needs to be possible to validate user input without saving it into the database. Example:
const input = {
email: "john@example.com"
};
// Validate user input and exit
const dryRun = true;
const [data, errors] = validate(input, (value) => ({
email_address: value("email")
.notEmpty({ if: !dryRun })
.isEmail(),
});
if (!dryRun && Object.keys(errors).length === 0) {
await db.table("customer").insert(data);
}
Adding Custom Rules
You can extend the built-in validator class with any additional validation rules (methods).
import { Validator } from "validator-fluent";
export class CoolValidator<K, V> extends Validator<K, V> {
constructor(key: K, value: V) {
super(key, value);
}
isLegit(): this {
if (!this.isEmpty && this.value !== "legit") {
this.errors.push("Not legit.");
}
return this;
}
}
import { validate, ValidationError } from "validator-fluent";
const input = { name: "???" };
const [data, errors] = validate(input, CoolValidator, (value) => ({
name: value("name").notEmpty().isLegit(),
}));
if (Object.key(errors).length > 0) {
throw new ValidationError(errors);
}
Top comments (0)