express-validator
is a powerful validation and sanitation library.
However, it relies on string property names for the validation to work.
This is not type and refactoring safe, hence error prone.
Let's see if we can fix this...
First, a litte example of how express-validator
works (from the official example):
import { body, validationResult } from "express-validator";
const express = require("express");
const app = express();
app.use(express.json());
app.post("/user", (req, res) => {
User.create({
username: req.body.username,
password: req.body.password,
}).then((user) => res.json(user));
});
app.post(
"/user",
// username must be an email
body("username").isEmail(),
// password must be at least 5 chars long
body("password").isLength({ min: 5 }),
(req, res) => {
// Finds the validation errors in this request and wraps them in an object with handy functions
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
User.create({
username: req.body.username,
password: req.body.password,
}).then((user) => res.json(user));
}
);
As you can see, body('username').isEmail()
is using the 'username'
string to define a validation rule.
This will become an issue if the username
property will be changed - if you don't have tests, your validation will be broken now.
But of course, there's a package for that: ts-simple-nameof
:
Parses a class name or a dot-separated property name from a lambda expression and provides some level of type safety using type parameters.
What does this mean exactly?
Here's a simple example:
type Comment = {
user: {
posts: Post[];
};
};
// Returns "posts".
nameof<Comment>((c) => c.user); // returns "user"
// Returns "user.posts".
nameof<Comment>((c) => c.user.posts);
So, ts-simple-nameof
returns the name of a property of a TypeScript type
, class
or interface
.
Having ts-simple-nameof
at hand, we now can solve our express-validator
refactoring issue like this:
type MyCommand = {
some: {
nested: {
property: string;
};
};
};
body(nameof<MyCommand>((c) => c.some.nested.property)).exists();
This will ensure, that a arbitrerily deeply nested property can be passed to the body
validation function in a refactoring safe way - neat!
A sample repo can be found here. It also shows how you can test express-validator
validations.