Mazzy
Mazzy

Reputation: 14179

Joi validation return only one error message

I have a three field form made of a name field, email field and a textarea. I'm using Joi 4.7.0 version along with hapijs. I use the object below validate the input. I receive the data object from an ajax call. When I fill all the three fields with wrong informations I get only the message relative to the first wrong field. Like that:

"{"statusCode":400,"error":"Bad Request","message":"name is not allowed to be empty","validation":    {"source":"payload","keys":["data.name"]}}"

validate: {
      payload: {
        data: {
          name: Joi.string().min(3).max(20).required(),
          email: Joi.string().email().required(),
          message: Joi.string().min(3).max(1000).required()
        }
      }
}

For explanation let suppose to not fill the three field. I get only one message error and not the message error of the others fields. Why?

Upvotes: 50

Views: 38219

Answers (5)

ushaikh
ushaikh

Reputation: 63

In hapi v21, we can add validate inside routes at the time of server instance creation, like this

const server = Hapi.server({
        port: 3000,
        host: 'localhost',
        routes: {
            validate: {
                options: {
                    abortEarly: false,           // Add this line
                },
            },
        },
    });

And inside a route, we can validate your payload with Joi

{
        method: 'POST',
        path: '/user',
        handler: async function() { },
        options: {
                validate: {
                        payload: Joi.object({
                                data: Joi.object({
                                        name: Joi.string().min(3).max(20).required(),
                                        email: Joi.string().email().required(),
                                        message: Joi.string().min(3).max(1000).required()
                                })
                        })
                }
        }
}

Upvotes: 0

Vyk
Vyk

Reputation: 151

After some research, I found out it can be solved 2 ways:

[Segments.BODY]: Joi.object().keys({
value: Joi.string().required().error(new Error('Value is required and has to be a text!')),
})

or

[Segments.BODY]: Joi.object().keys({
password: Joi.string().required().pattern(new RegExp('^[a-zA-Z0-9]{3,30}$')).min(8).label('Password').messages({
    'string.pattern.base': 'Your {#label} does not matche the suggested pattern',
    'string.base': `Your {#label} should match the suggested pattern`,
    'string.empty': `Your {#label} can not be empty`,
    'string.min': `Your {#label} has to be at least {#limit} chars`,
    'any.required': `Your {#label} is required`,
}),
})

Upvotes: 2

Stephen Paul
Stephen Paul

Reputation: 39005

I'm not integrating with hapi.js, but I noticed that there is a ValidationOptions object which can be passed along. Inside that object is an abortEarly option, so this should work:

Joi.validate(request, schema, { abortEarly: false }

This can also be configured as follows:

Joi.object().options({ abortEarly: false }).keys({...});

Check out these type definitions for more ValidationOptions: https://github.com/DefinitelyTyped/tsd/blob/master/typings/joi/joi.d.ts

Upvotes: 45

Gergo Erdosi
Gergo Erdosi

Reputation: 42048

It happens because Joi aborts early by default.

abortEarly - when true, stops validation on the first error, otherwise returns all the errors found. Defaults to true.

*EDIT: Configuration has changed in hapi 8.0. You need to add abortEarly: false to the routes config:

var server = new Hapi.Server();
server.connection({
    host: 'localhost',
    port: 8000,
    routes: {
        validate: {
            options: {
                abortEarly: false
            }
        }
    }
});

*See the Joi API documentation for more details.
*Also, see validation under Hapi Route options.

So Joi stops the validation on the first error:

var Hapi = require('hapi');
var Joi = require('joi');

var server = new Hapi.Server('localhost', 8000);

server.route({
    method: 'GET',
    path: '/{first}/{second}',
    config: {
        validate: {
            params: {
                first: Joi.string().max(5),
                second: Joi.string().max(5)
            }
        }
    },
    handler: function (request, reply) {

        reply('example');
    }
});

server.start();

server.inject('/invalid/invalid', function (res) {

    console.log(res.result);
});

Outputs:

{ statusCode: 400,
  error: 'Bad Request',
  message: 'first length must be less than or equal to 5 characters long',
  validation: { source: 'params', keys: [ 'first' ] } }

You can however configure Hapi to return all errors. For this, you need to set abortEarly to false. You can do this in server configuration:

var server = new Hapi.Server('localhost', 8000, { validation: { abortEarly: false } });

If you run the script now, you get:

{ statusCode: 400,
  error: 'Bad Request',
  message: 'first length must be less than or equal to 5 characters long. second length must be less than or equal to 5 characters long',
  validation: { source: 'params', keys: [ 'first', 'second' ] } }

Upvotes: 56

chris van
chris van

Reputation: 51

The validation key no longer works with the Hapi.Server constructor in Hapi 8.0:

[1] validation is not allowed

I found the solution in a GitHub issue for hapi:

var Hapi = require('hapi');


var server = new Hapi.Server();

server.connection({
  host: HOST,
  port: PORT,
  routes: {
    validate: {
      options: {
        abortEarly: false
      }
    }
  }
});

// Route using Joi goes here.
server.route({});

server.start(function () {
  console.log('Listening on %s', server.info.uri);
});

Upvotes: 5

Related Questions