Input validation using Joi in node.js application

Updated: Sep 18, 2021


 

Contents

  1. What is Joi?

  2. How to use Joi in node.js application to validate input from REST API?

 

What is Joi?

Joi is a data validator for JavaScript. Using Joi we can validate data in any JavaScript based frontend or node.js based backend application.


How to use Joi in node.js application to validate input from REST API?

joi-api: https://joi.dev/api/?v=17.4.0


This is the whole purpose of this post today. I will show with a code example how we can use Joi to validate the data that comes as input to the REST endpoints in an node.js application server.


The Project Structure

input_validators
 |-- UserValidator.js
model
 |-- User.js
routes
 |-- user.js
errorHandeler.js
responseHandler.js
validationHandler.js
node-app-server.js
package.json

The package.json

In the package.json we have to include the Joi as shown and install it using the npm install command

{
  . . .
  "dependencies": {
    "express": "^4.17.1",
    "joi": "^17.4.0",
  },
  . . .
}

The Router

We will apply the validation in the router just before the model is called to process the request and the response is generated. We will add a few lines to our user.js router that handles the user creation POST requests. For each entity like the user there will be a different set of input fields. We are using the UserValidator that contains the schema validation for user creation. The validationHandler is a generic piece of code that can execute the specific validators, here the Uservalidator.create method.

routes/user.js

const router = require('express').Router();
const processResponse = require('../responseHandler')();
const User = require('../model/User')();

const validate = require('../validationHandler')();
const UserValidator = require('../input_validators/UserValidator')();

module.exports = function () {
router.get('/user/:id', processRequest(User.fetch));
router.post('/user/:id', validate(UserValidator.create), 
    processResponse(User.create));
return router;
}

The Validation Handler

validationHandler.js

function validationHandler() {
return callback => validate(callback);
}

function validate(callback) {
return async(request, response, next) => {
try {
await callback(request, next);
} catch (error) {
next(error);
}
}
}

module.exports = validationHandler;

The User Validator

As mentioned above the user validator should contain all the validation schemas needed for different APIs serving User related requests. The create method defined the validation schema needed during user creation and does the validation. Here we use Joi schema. First we define the fields variable with content for the request body and the URL parameter. The validation will be applied on this variable. Then we define the validation schema using validation types and constraints for each field using the Joi.object(). Finally we apply the schema on the fields variable using the .validate() Joi method. The result is gathered in the constant named validation. The output will have two components, error and value. If the input is invalid, a TypeError object is created with details from error. An object with value is assigned to the value field if the input is valid, note the value will also contain the fields created with default values if not supplied with the actual input. The types and restrictions can be applied using method channing. They are self explanatory. For explanation and details see the API documentation of Joi.

input_validators/UserValidator.js

const Joi = require("joi");
const validRoles = [ "Admin", "Editor", "Reader", "None"];

function UserValidator() {
return Object.freeze({
create
});
async function create(request, next) {
let fields = request.body;
fields.id = request.params.id;

const validation = Joi.object({
id: Joi.string().trim().required(),
groupid: Joi.number().integer().positive().required(),
role: Joi.string().trim().valid(...validRoles).default("None"),
name: Joi.string().trim().required(),
privileged: Joi.boolean().default(false)
}).validate(fields, {
stripUnknown: true
});

if (validation.error) {
console.log("validation.error =>");
console.log(validation.error.details);
throw new TypeError(validation.error);
}

return next();
}
}
module.exports = UserValidator;

The Response Handler

See the response handler in the blog post Separate routing from business logic in node.js || Central response generation in node.js


Verification of the code

First we need to start our application, you can see my previous post to find out the minimal code needed to write an application server.

Let’s generate a POST request to create an user http://localhost:5000/user/abcd with body

{
    "groupid": 123,
    "role": "Admin",
    "name": "Dibyo"
}

It returns

"Validation Failed\"id\" must be a number"

with 400 HTTP status code.


Let's change the body

{
    "groupid": 123,
    "role": "Creator",
    "name": "Dibyo"
}

Now we get this as a response with HTTP status code 400.

"TypeError: ValidationError: \"role\" must be one of [Admin, Editor, Reader, None]"

This is a good example of validating inputs using Joi in a node.js application.



132 views0 comments

Recent Posts

See All