joi icon indicating copy to clipboard operation
joi copied to clipboard

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

Open redgumnfp opened this issue 3 years ago • 2 comments

Support plan

  • is this issue currently blocking your project? No, just a painful refactor
  • is this issue affecting a production system? No

Context

  • node version: V12
  • module version: 17.2.1
  • environment node
  • used with expressjs, mongoosejs, sequelizejs
  • any other relevant information: n/a

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,
}

redgumnfp avatar Dec 11 '20 18:12 redgumnfp

Any update or alternative for this?

devlegacy avatar Mar 24 '22 23:03 devlegacy

Unfortunately

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

is equivalent to

{ 

}

Not sure if that helps.

gagichce avatar Jun 09 '22 20:06 gagichce