Node.js + Express API - Request Schema Validation with Joi
This is a quick example of how to validate HTTP request data using the joi
schema validation library in a Node.js + Express API.
For more info about joi schema validation see https://www.npmjs.com/package/joi.
Installing joi from npm
With the npm CLI: npm install joi
With the yarn CLI: yarn add joi
Schema validation middleware for create account requests in Node.js with joi
This example Node.js middleware function validates a request to create a new account. It defines schema rules with the joi
schema validation library and calls schema.validate(req.body, options)
to determine if the request is valid.
Joi.string()
validates that all properties are stringsJoi.required()
makes all properties requiredJoi.email()
validates that the email property contains a valid email addressJoi.valid(Joi.ref('password'))
validates that the confirm password property matches the password propertyJoi.valid('Admin', 'User')
validates that the role property matches one of the specified roles ('Admin' or 'User')
function createAccountSchema(req, res, next) {
// create schema object
const schema = Joi.object({
title: Joi.string().required(),
firstName: Joi.string().required(),
lastName: Joi.string().required(),
email: Joi.string().email().required(),
password: Joi.string().min(6).required(),
confirmPassword: Joi.string().valid(Joi.ref('password')).required(),
role: Joi.string().valid('Admin', 'User').required()
});
// schema options
const options = {
abortEarly: false, // include all errors
allowUnknown: true, // ignore unknown props
stripUnknown: true // remove unknown props
};
// validate request body against schema
const { error, value } = schema.validate(req.body, options);
if (error) {
// on fail return comma separated errors
next(`Validation error: ${error.details.map(x => x.message).join(', ')}`);
} else {
// on success replace req.body with validated value and trigger next middleware function
req.body = value;
next();
}
}
Schema validation middleware for update account requests in Node.js with joi
This example Node.js middleware function validates a request to update an existing account.
It's similar to the create account schema above with a few small differences:
- The schema rules are created in a separate object before the schema object to allow for a conditional rule to be added only for admin users (
schemaRules.role
) - All properties are optional because none have the
Joi.required()
rule Joi.empty('')
tells joi to treat empty strings (''
) asundefined
so any property containing an empty string will be omitted from thevalue
returned byschema.validate(req.body, options)
.with('password', 'confirmPassword')
makes the confirmPassword property required only if the password property has a value, otherwise it is optional
function updateAccountSchema(req, res, next) {
// define base schema rules
const schemaRules = {
title: Joi.string().empty(''),
firstName: Joi.string().empty(''),
lastName: Joi.string().empty(''),
email: Joi.string().email().empty(''),
password: Joi.string().min(6).empty(''),
confirmPassword: Joi.string().valid(Joi.ref('password')).empty('')
};
// conditional schema rule - only admins can update role
if (req.user.role === 'Admin') {
schemaRules.role = Joi.string().valid('Admin', 'User').empty('');
}
// create schema object with rules
const schema = Joi.object(schemaRules)
// make confirmPassword required IF password is present
.with('password', 'confirmPassword');
// schema options
const options = {
abortEarly: false, // include all errors
allowUnknown: true, // ignore unknown props
stripUnknown: true // remove unknown props
};
// validate request body against schema
const { error, value } = schema.validate(req.body, options);
if (error) {
// on fail return comma separated errors
next(`Validation error: ${error.details.map(x => x.message).join(', ')}`);
} else {
// on success replace req.body with validated value and trigger next middleware function
req.body = value;
next();
}
}
Wrapping it up in a Node.js + Express controller
This example Node.js controller shows how the above schema validation middleware can be hooked up to routes with the Express router.
There are two routes defined:
- The create account route listens for
POST
requests to the/accounts/
path and is handled by the middleware functionscreateAccountSchema
followed bycreateAccount
- The update account route listens for
PUT
requests to the/accounts/:id
path and is handled by the middleware functionsupdateAccountSchema
followed byupdateAccount
The schema validation functions have been refactored and the common pieces have been moved into the validateRequest()
helper function, so the validation functions only contain schema rules for each route now.
The controller is a simplified version of the accounts controller from a boilerplate api project I posted recently, for more info and to test it in a fully functioning project see Node + Mongo - Boilerplate API with Email Sign Up, Verification, Authentication & Forgot Password.
const express = require('express');
const router = express.Router();
const Joi = require('joi');
const accountService = require('./account.service');
// routes
router.post('/accounts/', createAccountSchema, createAccount);
router.put('/accounts/:id', updateAccountSchema, updateAccount);
module.exports = router;
function createAccountSchema(req, res, next) {
const schema = Joi.object({
title: Joi.string().required(),
firstName: Joi.string().required(),
lastName: Joi.string().required(),
email: Joi.string().email().required(),
password: Joi.string().min(6).required(),
confirmPassword: Joi.string().valid(Joi.ref('password')).required(),
role: Joi.string().valid('Admin', 'User').required()
});
validateRequest(req, next, schema);
}
function createAccount(req, res, next) {
accountService.create(req.body)
.then(account => res.json(account))
.catch(next);
}
function updateAccountSchema(req, res, next) {
const schemaRules = {
title: Joi.string().empty(''),
firstName: Joi.string().empty(''),
lastName: Joi.string().empty(''),
email: Joi.string().email().empty(''),
password: Joi.string().min(6).empty(''),
confirmPassword: Joi.string().valid(Joi.ref('password')).empty('')
};
// only admins can update role
if (req.user.role === 'Admin') {
schemaRules.role = Joi.string().valid('Admin', 'User').empty('');
}
const schema = Joi.object(schemaRules).with('password', 'confirmPassword');
validateRequest(req, next, schema);
}
function updateAccount(req, res, next) {
accountService.update(req.params.id, req.body)
.then(account => res.json(account))
.catch(next);
}
// helper functions
function validateRequest(req, next, schema) {
const options = {
abortEarly: false, // include all errors
allowUnknown: true, // ignore unknown props
stripUnknown: true // remove unknown props
};
const { error, value } = schema.validate(req.body, options);
if (error) {
next(`Validation error: ${error.details.map(x => x.message).join(', ')}`);
} else {
req.body = value;
next();
}
}
Need Some NodeJS Help?
Search fiverr for freelance NodeJS developers.
Follow me for updates
When I'm not coding...
Me and Tina are on a motorcycle adventure around Australia.
Come along for the ride!