Let’s create ourselves router validation that is easy to lookup, easy to modify.
// routes/auth.js
/**
* @api {post} /api/auth/login
* @api AuthLogin
* @apiParam {string} email
* @apiParam {string} password
*
* Create new user session
*/
router.post(
"/login",
middleware.validateBody(
Joi.object().keys({
email: schemas.email.required(),
password: schemas.password.required(),
}),
),
authHandlers.login,
);
Our file tree:
src/
| routes/
| middleware.js
| schemas.js
Dependencies
First off install Joi:
npm install --save @hapi/joi
Middleware
I like to start with a middleware.js file. We fill create one function for each type of request validation - request body, url query params (url.query), url parameters url.params.
// /routes/middleware.js
/**
* Validate request contents against a joi schema.
* Returns http 422 if validation fails.
* @param {object} schema - Joi schema
* @param {object} data
* @param {object} res
* @param {function} next
*/
function validate(schema, data, res, next) {
const validation = schema.validate(data);
if (validation.error) {
res.status(422).json({
error: {
message: validation.error.details[0].message,
field: validation.error.details[0].context.key,
},
});
return;
}
next();
}
/**
* Validate request body
* @param {object} schema - Joi schema
*/
function validateBody(schema) {
return function validateWrapper(req, res, next) {
return validate(schema, req.body, res, next);
};
}
/**
* Validate GET request query
* @param {object} schema - Joi schema
*/
function validateQuery(schema) {
return function validateWrapper(req, res, next) {
return validate(schema, req.query, res, next);
};
}
/**
* Validate GET request parameters
* @param {object} schema - Joi schema
*/
function validateParams(schema) {
return function validateWrapper(req, res, next) {
return validate(schema, req.params, res, next);
};
}
module.exports = {
validateBody,
validateQuery,
validateParams,
};
Schemas
Now let’s write our Joi schemas. Let’s create a shared file where we define all our schemas. Why not write them inline? Imagine you have a subscription interval validation schema, you want this to be consistent across all endpoints.
// schemas.js
module.exports.email = Joi.string().email();
module.exports.password = Joi.string().min(8);
module.exports.subscriptionInterval = Joi.string().valid(["30", "90"]);
...
If this file grew too large I would split it up by domain into multiple files in a schemas/ folder.