Building Enterprise JavaScript Applications
上QQ阅读APP看书,第一时间看更新

Modularizing our validation logic

Next, create a new file at src/validators/users/create.js and copy the validation blocks from our request handlers into the file, wrapping it inside its own function and exporting that function:

function validate (req) {
if (
!Object.prototype.hasOwnProperty.call(req.body, 'email')
|| !Object.prototype.hasOwnProperty.call(req.body, 'password')
) {
res.status(400);
res.set('Content-Type', 'application/json');
return res.json({ message: 'Payload must contain at least the email and password fields' });
}
...
}

export default validate;

Next, import the ValidationError class from src/validators/errors/validation-error.js. Then, instead of modifying the res object (which is not in scope), return instances of ValidationError instead. The final src/validators/users/create.js file may look like this:

import ValidationError from '../errors/validation-error';

function validate(req) {
if (
!Object.prototype.hasOwnProperty.call(req.body, 'email')
|| !Object.prototype.hasOwnProperty.call(req.body, 'password')
) {
return new ValidationError('Payload must contain at least the email and password fields');
}
if (
typeof req.body.email !== 'string'
|| typeof req.body.password !== 'string'
) {
return new ValidationError('The email and password fields must be of type string');
}
if (!/^[\w.+]+@\w+\.\w+$/.test(req.body.email)) {
return new ValidationError('The email field must be a valid email.');
}
return undefined;
}

export default validate;

Next, we need to import this function into our request handler and use it to validate our Create User request payload. If the validation result is an instance of ValidationError, then generate the 400 response; otherwise, carry on with indexing the user document:

import ValidationError from '../../validators/errors/validation-error';
import validate from '../../validators/users/create';
function createUser(req, res, db) {
const validationResults = validate(req);
if (validationResults instanceof ValidationError) {
res.status(400);
res.set('Content-Type', 'application/json');
    return res.json({ message: validationResults.message });
}
db.index({ ... })
}

export default createUser;

By providing a common interface, we have successfully decoupled our validation logic from the rest of the code. Now, run the E2E tests, and if they're green, commit our changes!

$ git add -A && git commit -m "Decouple validation and response logic"