This question is more about code organisation rather than an error/bug problem.
I am working on request body validation, json has structure like:
{
"title": "Beetlejuice",
"year": "1988",
"runtime": "92",
"genres": [
"Comedy",
"Fantasy"
],
"director": "Tim Burton",
"actors": "Alec Baldwin, Geena Davis, Annie McEnroe, Maurice Page",
"plot": "A couple of recently deceased ghosts contract the services of a \"bio-exorcist\" in order to remove the obnoxious new owners of their house.",
"posterUrl": "https://images-na.ssl-images-amazon.com/images/M/MV5BMTUwODE3MDE0MV5BMl5BanBnXkFtZTgwNTk1MjI4MzE@._V1_SX300.jpg"
}
Although json is not big, still validation is quite large on simple POST request:
router.post('/api/movies/',
body('genres')
.isArray()
.withMessage('Property genres should be array of string.').bail()
.custom(validateGenres).bail(),
body('title')
.not().isEmpty()
.withMessage('Property title is required.').bail()
.isString()
.withMessage('Property title must be string.').bail()
.isLength({ max: 255 })
.withMessage('Property title must have maximum 255 characters.').bail(),
body('year')
.not().isEmpty()
.withMessage('Property year is required.').bail()
.isNumeric()
.withMessage('Property year must be number.').bail(),
body('runtime')
.not().isEmpty()
.withMessage('Property runtime is required.').bail()
.isNumeric()
.withMessage('Property runtime must be number.').bail(),
body('director')
.not().isEmpty()
.withMessage('Property director is required.').bail()
.isString()
.withMessage('Property director must be string.').bail()
.isLength({ max: 255 })
.withMessage('Property director must have maximum 255 characters.').bail(),
body('actors')
.isString()
.withMessage('Property actors must be string.').bail(),
body('plot')
.isString()
.withMessage('Property plot must be string.').bail(),
body('posterUrl')
.isString()
.withMessage('Property plot must be string.').bail(),
(req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).send(errors.array())
}
const movieCreated = movie.create(req.body);
return res.status(201).json(movieCreated);
})
The question here is: Is this large validation considered as bad practice? If yes, should I create a middleware which validates this particular json or what are possible architecture solutions to solve such refactoring problem?
the good practice here is that you are validating inputs ✅
the downside of your current approach is that it is hard to maintain long term ❌
you could go down the middleware way, and create multiple middlewares for each route/body that you need to validate. It works, but, the amount of work and maintenance burden will increase over time.
one approach you can follow is to create an expected schema of your input (e.g. a model definition describing the fields and values expected), and use a validator to check the current input based on the schema you created.
for Node.js we have multiple tools for that like: AJV, JOI, YUP.
using a sample example with JOI, you could replace your validation flow by doing:
// ./validation/post-create-user-schema.js
const Joi = require('joi');
const postCreateUserSchema = Joi.object({
username: Joi.string().alphanum().min(3).max(30).required(),
password: Joi.string().pattern(new RegExp("^[a-zA-Z0-9]{3,30}$")).required(),
repeat_password: Joi.ref("password").required(),
access_token: [Joi.string(), Joi.number()],
birth_year: Joi.number().integer().min(1900).max(2013),
email: Joi.string().email({
minDomainSegments: 2,
tlds: { allow: ["com", "net"] },
}),
});
module.exports = { postCreateUserSchema };
// ./routes.js
const { postCreateUserSchema } = require('./validation/post-create-user-schema.js')
router.post('/api/users/', (req, res) => {
try {
let json = JSON.parse(req.body);
postCreateUserSchema.validate(json);
} catch (err) {
// -> { value: {}, error: '"username" is required' }
console.error(err);
return res.status(400).send({ ok: false, message: err.error });
}
// ... route code here
});