Express-validator's default sanitizer doesn't work when chained

Viewed 805

I am having trouble using the default() sanitizer from express-validator. When I use it in a chain, such as body("children").optional().isArray().default([]), the default function doesn't do anything, resulting in children being undefined. However, if I separate these into two different statements (adding a separate body("children").default([]) sanitizer instead of chaining it with the others), it works perfectly fine. Here is a minimal reproducable example...

import express from "express";
import { body } from "express-validator";

const app = express();

app.use(express.json());

app.post(
  "/fails",
  body("children").isArray().optional().default([]),
  (req: express.Request) => {
    console.log(`/fails output: ${JSON.stringify(req.body, null, 4)}`);
  }
);

app.post(
  "/works",
  body("children").isArray().optional(),
  body("children").default([]),
  (req: express.Request) => {
    console.log(`/works output: ${JSON.stringify(req.body, null, 4)}`);
  }
);

app.listen(3000);

...that, used with these libraries...

@types/express@4.17.12
express-validator@6.12.0
express@4.17.1
typescript@4.3.4

...gives this output:

/fails output: {}
/works output: {
    "children": []
}

Both POST requests had an empty body (I am using Postman to test this).

Why is this happening? I have tried juggling the optional, isArray, and default checks around, with no result. Even though /works, well, works, I don't want to duplicate the same body statement twice for each optional default value I have in my schema.

Does anyone know how to fix this problem?

2 Answers

The issue is optional validator just completely ignores validation chain if value is undefined.

You have to decide whether you need children field as a default value [] even when it's completely missing in the body. And then remove optional. Or you may continue without it and then remove default.

When removing .optional() from the chain, it works. So this code:

import express from "express";
import { body } from "express-validator";

const app = express();

app.use(express.json());

app.post(
  "/fails",
  body("children").isArray().default([]),
  (req: express.Request) => {
    console.log(`/fails output: ${JSON.stringify(req.body, null, 4)}`);
  }
);

app.post(
  "/works",
  body("children").isArray().optional(),
  body("children").default([]),
  (req: express.Request) => {
    console.log(`/works output: ${JSON.stringify(req.body, null, 4)}`);
  }
);

app.listen(3000);

Gives the correct output for both /fails and /works. However, I have no idea why this happens! Why can't I do .optional().default([])? Why isn't this documented? I created a bug report on GitHub here if anyone is interested.

Related