Alexander Zeitler

Alexander Zeitler

Refactoring-safe nested validation with express-validator and ts-simple-nameof

Published on Tuesday, August 10, 2021

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.

What are your thoughts about "Refactoring-safe nested validation with express-validator and ts-simple-nameof"?
Drop me a line - I'm looking forward to your feedback! email
Imprint | Privacy