I made this library because writing the same thing twice sucks. I wanted to have a uniform API for data validation on the front-end and back-end.
I chose JavaScript objects as validation schemas because they’re simple and composable. Here is an example schema:
const basicInputSchema = {
minLength: 2,
maxLength: 20,
pattern: [/^[A-Za-z]*$/, "You can only include letters"],
};
const validationSchema = {
firstName: basicInputSchema,
lastName: basicInputSchema,
username: {
...basicInputSchema,
pattern: [/^\w*$/, "You can only include letters, digits and underscores"],
},
password: {
...basicInputSchema,
pattern: [/\d/, "Password must contain a digit"],
},
};
Custom validation is an essential feature, so I tried to make it as straightforward as possible.
const validationSchema = {
// ...
repeatPassword: {
custom: (repeatPassword, { data }) => {
if (repeatPassword !== data.password) {
return Promise.reject("Passwords must match");
}
},
},
};
You can embed your custom validators into the library and create dynamic error messages.
config({
validators: {
sameAs: [
(val, { limit, message, data }) => {
if (val !== data[limit]) {
return Promise.reject(message);
}
},
(limit, field) => `${field} is different than ${limit}`,
],
},
});
Checc.js comes with an Express middleware out of the box.
const checc = require("checc/middleware");
app.post("/", [
checc(
"body",
{
username: {
custom: async (username, { next }) => {
const user = await User.findOne({ username }).catch(next);
if (user) return Promise.reject("Username taken");
},
},
},
{ keepSchema: false }
),
(req, res) => {
const { isValid, errors } = req.checc.body;
if (!isValid) return res.status(400).json(errors);
res.sendStatus(200);
},
]);
I made this small library quite some time ago. If I were to write it today, I would change some things. For example, I’d write it in TypeScript.
There’s a couple more issues, however, we already have some good validation libraries in the TypeScript ecosystem. It was a fun project though.