35

I want to validate my Express routes before calling the controller logic. I use joi and created a validator which is able to validate the Request object against the schema object

import { Request, Response, NextFunction } from 'express';
import joi, { SchemaLike, ValidationError, ValidationResult } from '@hapi/joi';
import { injectable } from 'inversify';

@injectable()
export abstract class RequestValidator {
    protected validateRequest = (validationSchema: SchemaLike, request: Request, response: Response, next: NextFunction): void => {
        const validationResult: ValidationResult<Request> = joi.validate(request, validationSchema, {
            abortEarly: false
        });

        const { error }: { error: ValidationError } = validationResult;

        if (error) {
            response.status(400).json({
                message: 'The request validation failed.',
                details: error.details
            });
        } else {
            next();
        }
    }
}

Next I created a deriving class which creates the validationSchema and calls the validateRequest method. For the sake of simplicity I will show the "deleteUserById" validation

import { Request, Response, NextFunction } from 'express';
import joi, { SchemaLike } from '@hapi/joi';
import { injectable } from 'inversify';

import { RequestValidator } from './RequestValidator';

@injectable()
export class UserRequestValidator extends RequestValidator {
    public deleteUserByIdValidation = async (request: Request, response: Response, next: NextFunction): Promise<void> => {
        const validationSchema: SchemaLike = joi.object().keys({
            params: joi.object().keys({
                id: joi.number().required(),
            })
        });

        this.validateRequest(validationSchema, request, response, next);
    }
}

Important note: I create the SchemaLike that way because some routes might have params, body, query which need to get validated in one run.

When calling the Route

DELETE /users/1

the validation always fails. I get this error

UnhandledPromiseRejectionWarning: TypeError: joi_1.default.validate is not a function

The error occurs with every validation, whether called correctly or not. Does someone know how to fix it?

17 Answers17

31

You can fix it by changing:

joi.validate(request, validationSchema)

to:

validationSchema.validate(request)

joi.validate() Is no longer supported in v16. It is clearly documented in the API docs and release notes.

Mohammadreza Khedri
  • 2,523
  • 1
  • 11
  • 22
Eran Hammer
  • 7,036
  • 3
  • 31
  • 23
22

Update version of joi doesn't work with Joi.validate(req.body,schema); No need to use the object separately. Use this as following. It Worked for me smoothly. Do let me know if I am wrong at anything:

const Joi = require('@hapi/joi');

const schema = Joi.object({
    name:Joi.string().min(3).required(),
    email:Joi.string().min(4).required().email(),
    password:Joi.string().min(6).required()
});





router.post('/register', async (req,res) => {


    //updated joi
    
   // This is a shorter version
    const { error } = schema.validate(req.body);

    // Error in response

    res.send(error.details[0].message);


    // WORKING WITH DEPRECATED VERSION
    // const Validation = Joi.validate(req.body,schema);
    // res.send(Validation);
Vaaneet Kapoor
  • 256
  • 1
  • 3
14

I experienced joi.validate() is not a function also. I checked their documentation and got a fix for it.

    const Joi = require('@hapi/joi');

const schema = Joi.object({
    name:Joi.string().min(6).required(),
    email:Joi.string().min(6).required().email(),
    password:Joi.string().min(6).required()
});

router.post('/register',  (req, res) => {

    // VALIDATE BEFORE SAVING A USER 
    const Validation = schema.validate(req.body);
    res.send(Validation);

    
})

This works as expected and gives no further error.

BeardedPrince
  • 210
  • 2
  • 8
13

Hit the same problem when using express-joi-validation.

If it's ok for you to use version 15, downgrade Joi will make it.

npm uninstall --save @hapi/joi
npm install --save @hapi/joi@15.0.3
below-1
  • 354
  • 5
  • 9
7

You can fix it by changing Joi.validate(request, validationSchema) to validationSchema.validate(request) As Joi.validate() is no longer supported in v16.

For new version

const schema = Joi.object({ name: Joi.string() .min(6) .required(),
email: Joi.string() .min(6) .required() .email(),
password: Joi.string() .min(6) .required() });

const validation = schema.validate(req.body);
Sumith Ekanayake
  • 1,741
  • 17
  • 13
5

Instead of downgrading the Joi version it's better to quickly have a look at API of the newest version and check the correct way of using it.

Here is the link to the latest Joi API at the moment (16.1.7), where you can see an example of using validate.

Also to make sure you know what has been changed in next version of a lib that you use, it is good to have a look at the Release notes, here is a link to a Joi version 16 Release notes where you can see all the changes/new features, and FYI you can see info about validate method:

Remove Joi.validate() and Joi.describe() (call direct on schema instead) (#1941)

komron
  • 2,267
  • 2
  • 17
  • 26
  • 1
    This answer worked. In schema put const schemaName= Joi.object({ and then your normal schema data: Note the Joi.object is needed. When calling it, just use schemaName.validate(req.body) [instead of the old version Joi.validate(req.body, schemaName)] – Nhon Ha Oct 14 '19 at 04:19
3

For the new version

const schema = Joi.object({ name: Joi.string() .min(6) .required(),
email: Joi.string() .min(6) .required() .email(),
password: Joi.string() .min(6) .required() });

const validation = schema.validate(req.body);
res.send(validation);
kukab
  • 561
  • 5
  • 18
1

Here are some fixed with snippet codes :)

Install below npm package.

npm install --save @hapi/joi@15.0.3

req-validator.js file

const Joi = require("@hapi/joi");

const registerValidation = data => {

const schema = {
    name : Joi.string().min(4).required(),
    email: Joi.string().min(4).required().email(),
    password: Joi.string().min(4).required()
};
return Joi.validate(data,schema);

}
module.exports.registerValidation = registerValidation;

finally, calling the validation method in the controller.

const router = require("express").Router();
const {registerValidation}  = require("./req-validator")

router.post("/register",async(req,res)=> {

const {error} =  registerValidation(req.body);
if(error){
    return res.status(400).send(error.details[0].message);
}
 // logic to save in DB....
}
Ajay Kumar
  • 4,864
  • 1
  • 41
  • 44
1

Simply You can check whether you have @hapi/joi installed and required in your file

and for validation use This approach.

const schema= Joi.object().keys({

    name: Joi.string().min(6).required(),
    email: Joi.string().min(6).email(),
    password: Joi.string().min(6).required()
});

 const validation = schema.validate(req.body);
    if(validation.error){
        res.status(400).send(validation.error.details[0].message);
        return ;
    }

I think This is gonna work

twizelissa
  • 111
  • 5
1
Following works with the latest version ("@hapi/joi": "^17.1.1"):

    const Joi = require("@hapi/joi");

    const schema = Joi.object({
      name: Joi.string().min(6).required(),
      email: Joi.string().min(6).required().email(),
      password: Joi.string().min(6).required(),
    });
    
  router.post("/register", async (req, res) => {
  const { error } = schema.validate(req.body);

  if (error) {
    res.json({ ErrorMessage: error.details[0].message });
  } else {
    res.json({ message: "valid data" });
  }

});
Sakshi
  • 41
  • 2
1

npm i joi

joi v17.4.x resource: https://joi.dev/api/?v=17.4.2

const schema = Joi.object({
  email: Joi.string().email().required(),
  password: Joi.string().required().min(5),
});

 const result = schema.validate(YourRequest.body);
SL codegen
  • 41
  • 2
  • Your formatting is confusing me so much that I cannot tell wether this is code only or a "test" random characters post. Lets assume code-only. – Yunnosch Oct 27 '21 at 12:33
  • While this code may solve the question, [including an explanation](//meta.stackexchange.com/q/114762) of how and why this solves the problem would really help to improve the quality of your post, and probably result in more up-votes. Remember that you are answering the question for readers in the future, not just the person asking now. Please [edit] your answer to add explanations and give an indication of what limitations and assumptions apply. – Yunnosch Oct 27 '21 at 12:34
  • 1
    Linked content is not considered part of an answer, so this is still code-only. And unreadable or confusingly formatted. Please write some prose with explanation and try for more somber formatting. – Yunnosch Oct 27 '21 at 12:36
  • As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Oct 27 '21 at 14:05
0

Create your validate function like this

const validateUser = function (user) {
  const schema = Joi.object({
    username: Joi.string().min(5).max(50),
    email: Joi.string().required().email().min(5).max(255),
    password: Joi.string().required().min(5).max(1024),
  });

  return schema.validate(user);
};

You will get the error by

const { error } = validateUser({ email: 'admin' });

And the error message by

const message = error.details[0].message;
the_haystacker
  • 1,585
  • 18
  • 20
0

// Get the instance on schema defination

function validate(model, object, scope) {
  const schema = getSchema(model, scope);
  return schema.validate(object);
}

// actual middleware factory to validate

module.exports = function ValidationMiddleware(model, scope) {
  return (req, res, next) => {
    const validationResult = validate(model, req.body, scope);
    if (validationResult.error) {
      throw new Error(validationResult.error.details[0].message);
    } else {
      next();
    }
  };
};
  • While this code may answer the question, code-only answers have limited usefulness for future visitors to this question. Consider editing in some explanation on how and/or why your code answers the question. – Marsroverr Nov 16 '20 at 20:14
0

use this

const schema = Joi.object({
    name: Joi.string().min(6).required(),
    email: Joi.string().min(6).email(),
    password: Joi.string().min(6).required()
})

insted of const schema = {...}

and Validate through this:

const validate = schema.validate(req.body)

instead of Joi.validate(req.body,schema)

0

Here is something straight out of a project I'm working on, using Joi 17.3.0:

PDFCreatorController.exportAsPDF = (req, res) => {
  const data = req.body
  const schema = Joi.object().keys({
    svgDataToExport: Joi.string().required()
  })
  const isValidPromise = new Promise((resolve) => {
    const validation = schema.validate(req.body)
    if (validation.error) {
      res.status(422).json({
        status: 'error',
        message: error,
        data: data
      })
    } else {
      resolve(data)
    }
  })
  isValidPromise.then(inputDataFromUser => {
    try {
      const exportAsPDF = PDFCreatorModel.exportAsPDF(inputDataFromUser)
      exportAsPDF.then(data => {
        res.json(data)
      })
    } catch (error) {
      console.error(error)
      return res.status(500).json(error)
    }
  })
}
Wayne Smallman
  • 1,690
  • 11
  • 34
  • 56
0

Due to Updated version of JOI Package we are all facing this error but here is the solution of that. In Node.js when we facing this error we can solve it applying below solution.

// Example : Here we try to validate user's data
// User's POST Api 

const schema = Joi.object({
    name: Joi.string().min(1).required(),
    email:  Joi.string().required().email(),
    password: Joi.string().min(8).max(12).required(),
    isEmailVerified: Joi.boolean().required(),
    forgotPasswordToken: Joi.string().required(),
    isDeleted: Joi.boolean().required()
});
try {
    const value = await schema.validateAsync(req.body);
}
catch (err) {
    return res.status(400).send(err);
 }
David Buck
  • 3,752
  • 35
  • 31
  • 35
0

Lower down the version of joi. to 13.0.1 it will work. in terminal npm i joi@13.0.1. 2.after install check. dependencies in package. Json file.

Sanush
  • 81
  • 1
  • 2