Mark all properties as required or not Joi

徘徊边缘 提交于 2021-01-07 03:09:01

问题


I'm building an Express API and using @hapi/joi for validation. However I find myself in the following situation: if I want to validate a new user, all the properties in the schema have to be required, but if the client wants to modify a user, these properties must be optional since only the client knows what properties it will modify. So if I have this schema for new users :

function validateUser(user) {
  const schema = Joi.object().keys({
    name: Joi.string().required(),
    lastName: Joi.string().required(),
    email: Joi.string().email().required(),
    password: Joi.string().required(),
    address: Joi.string().required(),
    city: Joi.string().required(),
    district: Joi.string().required()
});

  return schema.validate(user);
}

Since all the properties are required, when I want to use a schema with the same properties but all optional, I have to hard code another schema where all properties are optional. Is there a way or an option of Joi to avoid redundant code? Like an isNew parameter passed to the validateUser function to apply it to the schema?


回答1:


I guess you are tackling the problem in a wrong way. Schema should generally depict how your types are in DB. for example if a first name is required, it will always be required irresepective if it is create or update. So in case of update you will fetch the item from db and apply changes on top of that and then send it for validation.

If you really want to do it this way, you could pass context to your validate function which can be accessed from your joi schema using $ sign. You have two choices here, either apply if else condition for each property which will make your schema look ugly OR have all togather two version of schema and choose depending upon value of the context passed. Something like this

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

const schemaA = Joi.object().keys({
  "name": Joi.string().required(),
  "lastName": Joi.string().required(),
  "email": Joi.string().email().required(),
  "password": Joi.string().required(),
  "address": Joi.string().required(),
  "city": Joi.string().required(),
  "district": Joi.string().required()
});

const schemaB = Joi.object().keys({
  "name": Joi.string(),
  "lastName": Joi.string(),
  "email": Joi.string().email(),
  "password": Joi.string(),
  "address": Joi.string(),
  "city": Joi.string(),
  "district": Joi.string()
});

function ChooseSchema() {
  return Joi.when(Joi.ref("$isNew"), {
    "is": true,
    "then": schemaA,
    "otherwise": schemaB
  });
}

console.log(ChooseSchema().validate({"name": "ashish"}, {"context": {"isNew": true}}));
console.log(ChooseSchema().validate({"name": "ashish"}, {"context": {"isNew": false}}));





回答2:


A better way will be using any.fork(paths, adjuster).

Returns a new schema where each of the path keys listed have been modified where:

  • paths - an array of key strings, a single key string, or an array of arrays of pre-split key strings. Key string paths use dot . to indicate key hierarchy.
  • adjuster - a function using the signature function(schema) which must return a modified schema. For example, (schema) => schema.required().

The method does not modify the original schema.

ex:


// define the object

const schemaObj = {
    name: Joi.string(),
    lastName: Joi.string(),
    email: Joi.string().email(),
    password: Joi.string(),
    address: Joi.string(),
    city: Joi.string(),
    district: Joi.string()
};

// no need to use fork here every obj is already not required
const allUnreqJoiObj = Joi.object(schemaObj);

// use of fork to modify it

// fork for all keys
const allReqJoiObj = Joi.object(schemaObj).fork(Object.keys(schemaObj), (schema) => schema.required());
console.log(allReqJoiObj.validate({'city': 'some city'}));


// fork for selected keys 
const fewReqJoiObj = Joi.object(schemaObj).fork(['email', 'password'], (schema) => schema.required());
console.log(fewReqJoiObj.validate({'city': 'some city'}));

if you already have a schema where some or all the keys is required then use fork and make them optional

ex:

const schemaObj = {
  name: Joi.string().required(),
  lastName: Joi.string().required(),
};

const allOptional = Joi.object(schemaObj).fork(Object.keys(schemaObj), (schema) => schema.optional());


const nameOptional = Joi.object(schemaObj).fork(['name'], (schema) => schema.optional());



来源:https://stackoverflow.com/questions/60578037/mark-all-properties-as-required-or-not-joi

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!