Search code examples
javascriptnode.jstypescriptexpressexpress-validator

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


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?


Solution

  • 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.