hapijs / joi

The most powerful data validation library for JS
Other
20.91k stars 1.51k forks source link

.default().allow() and undefined; consistency in emitted key-value pairs #2525

Open redgumnfp opened 3 years ago

redgumnfp commented 3 years ago

Support plan

Context

What problem are you trying to solve?

I'm looking to be able to have a consistent object containing undefined values for the keys provided/validated.

This allows a developer to plug the validated values object directly into a method, and ensure that a key-value is set, even if that value is undefined.

For example:

const Joi = require('joi');

const req = {
  mobile: null,
  landline: undefined,
  email: '',
}

const JoiSchema = Joi
  .object().keys({
    mobile: Joi
      .string()
      .trim()
      .empty(['', null]),
    landline: Joi
      .string()
      .trim()
      .empty(['', null]),
    email: Joi
      .string()
      .trim()
      .empty(['']),
  })
  .required();

const joiValidation = async (schema, req) => schema.validateAsync(req, {nonEnumerables: true})
 .then(payload => {
    console.log('payload', payload)
  })
  .catch(({ details }) => {
  console.log('details', details)
  });

joiValidation(JoiSchema, req)

This basic Joi schema would strip out blank and null values, including trimmed strings. The aim being to work with a consistent undefined variable, rather than blank/null.

Of note, on the above example, landline would still pass through as undefined as it's not modifed by Joi, and is optional. This confuses what Joi's responsibility is.

Mongoose/Sequelize

If I was using mongooseJs, or Sequelize, I could pass the result into an update method, without having to duplicate the effort of typing out the keys again.

For example: await User.findByIdAndUpdate( id, payload, );

If payload doesn't contain the key, it would not set the value, which is logical, but as the key doesn't exist, it means that in the above case, only landline would be updated (as undefined is passed-through and exists as a key).

If I wanted to set my value in MongoDb to undefined, I would have to write it as per:

await User.findByIdAndUpdate( id, { email, mobile, landline, } );

Alternatively, I could set it to null, with: .default(null)

The separate issue with this is that not all fields in a database, or function would allow null; for example if I was to pass a "limit" parameter to MongoDb (you could argue that MongoDb could associate null as undefined mind).

So this request is primarily about standards, and a parameter option to keep key-value pairs for undefined values.

This would improve code-maintenance within projects that use Joi. For example, if I was to add a new field, "email2", I would need to add it into my schema, and mongoose/sequelize functions, rather than just the Joi schema, with it passing into the pre-existing function as an object.

Do you have a new or modified API suggestion to solve the problem?

As it is not always the case that undefined should update the data-layer, I would suggest a parameter/option to "keepAllKeys" on the validate/validateAsync function, or for the allow()/default() functions to work with undefined, with that value being emitted as a key, for example:

The basic output I'm seeking is an object such as:

{
    email: undefined,
    landline: undefined,
    mobile: undefined,
}
devlegacy commented 2 years ago

Any update or alternative for this?

gagichce commented 2 years ago

Unfortunately

{
    email: undefined,
    landline: undefined,
    mobile: undefined,
}

is equivalent to

{ 

}

Not sure if that helps.