Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Validation as middleware too distant from dependent code? #1187

Open
jallen-cse opened this issue Nov 17, 2022 · 6 comments
Open

Validation as middleware too distant from dependent code? #1187

jallen-cse opened this issue Nov 17, 2022 · 6 comments

Comments

@jallen-cse
Copy link

jallen-cse commented Nov 17, 2022

Hello all,

I am relatively new to express validator. I have been trying it out in development of a simple RESTful interface. My project is organized into the conventional [routes, controllers, services] structure.

I feel that performing validation with middleware causes the validation code to be far away from the code that relies upon the validation. This worries me from a maintenance standpoint.


An example:

// routes/users.routes.js

usersRouter.get("/:userId", param("userId").isInt(), usersController.getUser);

then

// controllers/users.controller.js

async function getUser(req, res) {
    // should we really trust req.params.userId now?
    const userIdAsNumber = parseInt(req.params.userId);
}

In other cases (like for query parameters), if we are using TypeScript, we still have to break the safety and coerce into our sanitized, validated type.

// controllers/users.controller.js

async function getUser(req, res) {
    // TypeScript complains b/c it can't be sure of req.query.userId type
    const userIdAsNumberFromQueryParam = parseInt(req.query.userId);
}

I have thought about validating imperatively as mentioned in the docs, but that doesn't help with the TypeScript stuff.


Does anyone have any recommendations / solutions to these two concerns? Maybe I'm just missing something here.

Thanks in advance.

-Jack

@aAsdhir
Copy link

aAsdhir commented Nov 19, 2022

Hey @jallen-cse

// should we really trust req.params.userId now?
No you should not. You still need to use validationResult imported from express-validator to check for existence of validation errors.

'I feel that performing validation with middleware causes the validation code to be far away from the code that relies upon the validation.'
Controllers rely on the data and the best way to keep things simple is to validate your data before passing it onto the controller so that the controller doesn't have to worry about "dirty" data

@jallen-cse
Copy link
Author

// should we really trust req.params.userId now? No you should not. You still need to use validationResult imported from express-validator to check for existence of validation errors.

It's my fault for asking this poorly, I omitted the result call in my example. I meant that the validation logic is not anywhere near the consuming logic in the controller. From a maintenance perspective, I feel this is unsafe. If the controller's requirements change, you must be sure that the validation logic (located elsewhere) is changed accordingly.

Controllers rely on the data and the best way to keep things simple is to validate your data before passing it onto the controller so that the controller doesn't have to worry about "dirty" data

I don't mean to be argumentative, but this is simply an assertion that I am questioning in the original post.

@gustavohenke
Copy link
Member

gustavohenke commented Dec 17, 2022

I understand your frustration @jallen-cse 😕

Unfortunately even matchedData won't give you a strongly typed value.

There's no way, at the moment, for express-validator to infer the types when returning you the data that you validated.
That's because there's no single function that returns and/or augments a generic type based on the existing validations.

Something I've been thinking about is to have a single middleware that holds all the validations, and you can also invoke functions like matchedData, validationResult and etc on/from it.
It'd look something like this:

type UserData = { name: string, age?: number, /* etc etc */ };
const userValidator: Validator<UserData> = createValidator(
	body('name').notEmpty(),
	body('age').optional().isInt({ min: 18 }),
);
app.post('/user', userValidator, (req, res) => {
	try {
		const data: UserData = userValidator.getData(req);
	} catch (e) {
		// invalid request
	}
});

@lutuantai95
Copy link

I had same issue. I checked express-validator that it didn't work on Router:

const router = express.Router();
router.get(
  "/:userId",
  param("userId").isInt(),
  (req, res) => {
    res.send(`Hello!`);
  }
);
app.use(router);

@soham0005
Copy link

Try something like this, it might work

const { param, validationResult } = require("express-validator");
router.get("/:userId", [
  param("userId").isInt(),
], (req, res) => {
  const errors = validationResult(req);
  if (!errors.isEmpty()) {
    // Handle validation errors
    return res.status(400).json({ errors: errors.array() });
  }

  // Proceed with the route logic if validation passes
  res.send("Hello!");
})

@lutuantai95
Copy link

@soham0005
Thanks, it worked. But i wanna only validate at param("userId").isInt() to clean code (don't use validationResult)
😄

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants