# joi

***

**1. Basic Validation**

```js
const Joi = require('joi');

const schema = Joi.object().keys({
  name: Joi.string().required(),
  age: Joi.number().min(18).max(100),
});

const data = { name: 'John', age: 30 };

const { error, value } = schema.validate(data);

if (error) {
  console.log(error.message);
} else {
  console.log(value); // { name: 'John', age: 30 }
}
```

**2. Nested Validation**

```js
const schema = Joi.object().keys({
  user: Joi.object().keys({
    name: Joi.string().required(),
    email: Joi.string().email().required(),
  }).required(),
  address: Joi.object().keys({
    street: Joi.string().required(),
    city: Joi.string().required(),
    state: Joi.string().length(2).required(),
  }).required(),
});

const data = {
  user: {
    name: 'John',
    email: 'john@example.com',
  },
  address: {
    street: '123 Main Street',
    city: 'Anytown',
    state: 'CA',
  },
};

const { error, value } = schema.validate(data);

if (error) {
  console.log(error.message);
} else {
  console.log(value); // { user: {...}, address: {...} }
}
```

**3. Array Validation**

```js
const schema = Joi.object().keys({
  arr: Joi.array().items(
    Joi.object().keys({
      name: Joi.string().required(),
      value: Joi.number().optional(),
    })
  ),
});

const data = {
  arr: [
    { name: 'Item 1', value: 10 },
    { name: 'Item 2', value: 20 },
  ],
};

const { error, value } = schema.validate(data);

if (error) {
  console.log(error.message);
} else {
  console.log(value); // { arr: [{...}, {...}] }
}
```

**4. Custom Error Messages**

```js
const schema = Joi.object().keys({
  age: Joi.number().min(18).error(new Error('Must be at least 18')),
});

const data = { age: 16 };

const { error, value } = schema.validate(data);

if (error) {
  console.log(error.message); // Must be at least 18
}
```

**5. Custom Validation**

```js
Joi.extend(joi => ({
  base: joi.type(),
  name: 'string',
  language: {
    notContainsSubstring: 'does not contain the substring {{substring}}',
  },
  rules: [
    {
      name: 'notContainsSubstring',
      params: {
        substring: joi.string().required(),
      },
      validate(params, value, state, options) {
        if (value.indexOf(params.substring) !== -1) {
          return this.createError('string.notContainsSubstring', {
            value,
            substring: params.substring,
          }, state, options);
        }

        return value;
      },
    },
  ],
}));

const schema = Joi.string().notContainsSubstring('bad');

const data = 'This is a good string';

const { error, value } = schema.validate(data);

if (error) {
  console.log(error.message); // does not contain the substring bad
}
```

**6. Async Validation**

```js
const schema = Joi.object().keys({
  name: Joi.string().required(),
  email: Joi.string().email().required(),
});

const validate = async data => {
  const { error, value } = await schema.validateAsync(data);

  if (error) {
    throw error;
  }

  return value;
};
```

**7. Property Inclusion/Exclusion**

```js
const schema = Joi.object().keys({
  name: Joi.string().required(),
}).with('name', 'age');

const data1 = { name: 'John' }; // Valid
const data2 = { age: 30 }; // Invalid

const { error, value } = schema.validate(data1);

if (error) {
  console.log(error.message); // age is required
}
```

**8. Mutual Exclusion**

```js
const schema = Joi.object().keys({
  name: Joi.string(),
  age: Joi.number(),
  location: Joi.string().when('type', {
    is: 'home',
    then: Joi.required(),
    otherwise: Joi.optional(),
  }),
});

const data1 = { name: 'John', type: 'home' }; // Valid
const data2 = { name: 'Mary', age: 30 }; // Valid

const { error, value } = schema.validate(data1);

if (error) {
  console.log(error.message); // location is required
}
```

**9. Validating Alternative Types**

```js
const schema = Joi.alternatives().try(
  Joi.string(),
  Joi.number(),
  Joi.boolean(),
);

const data1 = 'Hello'; // Valid
const data2 = 123; // Valid
const data3 = true; // Valid

const { error, value } = schema.validate(data1);

if (error) {
  console.log(error.message); // only string, number, or boolean is allowed
}
```

**10. Validating an Array with Specific Maximum or Minimum Length**

```js
const schema = Joi.array().min(3).max(5);

const data1 = [1, 2, 3]; // Valid
const data2 = [1, 2, 3, 4, 5]; // Valid
const data3 = [1, 2]; // Invalid

const { error, value } = schema.validate(data3);

if (error) {
  console.log(error.message); // should have at least 3 items
}
```

**11. Validating an Object with a Specific Number of Keys**

```js
const schema = Joi.object().length(2);

const data1 = { name: 'John', age: 30 }; // Valid
const data2 = { name: 'Mary' }; // Invalid

const { error, value } = schema.validate(data2);

if (error) {
  console.log(error.message); // should have 2 keys
}
```

**12. Validating an Object with Specific Key Ordering**

```js
const schema = Joi.object().keys({
  name: Joi.string().required(),
  age: Joi.number().min(18).max(100).required(),
}).pattern(/^age/, Joi.number().min(18).max(100));

const data1 = { name: 'John', age: 30 }; // Valid
const data2 = { age: 30, name: 'John' }; // Invalid

const { error, value } = schema.validate(data2);

if (error) {
  console.log(error.message); // should have key 'name' before key 'age'
}
```

**13. Validating an Object with a Specific Key Presence**

```js
const schema = Joi.object().with('name', 'age');

const data1 = { name: 'John', age: 30 }; // Valid
const data2 = { name: 'Mary' }; // Invalid

const { error, value } = schema.validate(data2);

if (error) {
  console.log(error.message); // 'age' is required if 'name' is present
}
```

**14. Validating an Object with a Specific Key Absence**

```js
const schema = Joi.object().without('name', 'email');

const data1 = { age: 30 }; // Valid
const data2 = { name: 'John', age: 30 }; // Invalid

const { error, value } = schema.validate(data2);

if (error) {
  console.log(error.message); // 'email' is not allowed if 'name' is present
}
```

**15. Validating an Object with Permitted Properties Only**

```js
const schema = Joi.object().keys({
  name: Joi.string().required(),
  age: Joi.number().min(18).max(100).optional(),
}).unknown(false);

const data1 = { name: 'John', age: 30 }; // Valid
const data2 = { name: 'Mary', age: 30, job: 'developer' }; // Invalid

const { error, value } = schema.validate(data2);

if (error) {
  console.log(error.message); // object contains unexpected key: job
}
```

**16. Validating an Object with Conditional Properties**

```js
const schema = Joi.object().when('type', {
  is: 'admin',
  then: Joi.object({
    name: Joi.string().required(),
    email: Joi.string().email().required(),
  }),
  otherwise: Joi.object({
    name: Joi.string().optional(),
    email: Joi.string().optional(),
  }),
});

const data1 = { type: 'admin', name: 'John', email: 'john@example.com' }; // Valid
const data2 = { type: 'user', name: 'Mary', email: 'mary@example.com' }; // Valid

const { error, value } = schema.validate(data1);

if (error) {
  console.log(error.message); // name is required for type admin
}
```

**17. Validating an Object with Combined Conditions**

```js
const schema = Joi.object().when('type', {
  is: 'admin',
  then: Joi.object({
    name: Joi.string().required(),
    email: Joi.string().email().required(),
  }).optional(),
  otherwise: Joi.object({
    name: Joi.string().optional(),
    email: Joi.string().email().optional(),
  }),
}).when('role', {
  is: 'super_admin',
  then: Joi.object({
    name: Joi.string().required(),
    email: Joi.string().email().required(),
  }),
});

const data1 = { type: 'admin', name: 'John', email: 'john@example.com' }; // Valid
const data2 = { role: 'super_admin', name: 'Mary', email: 'mary@example.com' }; // Valid

const { error, value } = schema.validate(data1);

if (error) {
  console.log(error.message); // name is required for type admin and role super_admin
}
```

**18. Validating a File Upload**

```js
const schema = Joi.object({
  avatar: Joi.any().meta({ swaggerType: 'file' }),
});

// Swagger documentation
/**
 * @swagger
 * components:
 *   schemas:
 *     AvatarUpload:
 *       type: object
 *       required: [avatar]
 *       properties:
 *         avatar:
 *           type: file
 *           description: Profile picture of the user
 */
```

**19. Validating a Nested Object**

```js
const schema = Joi.object().keys({
  user: Joi.object().keys({
    name: Joi.string().required(),
    email: Joi.string().email().required(),
    address: Joi.object().keys({
      street: Joi.string().required(),
      city: Joi.string().required(),
      state: Joi.string().required(),
    }),
  }),
});

const data = {
  user: {
    name: 'John Doe',
    email: 'john@example.com',
    address: {
      street: '123 Main Street',
      city: 'Anytown',
      state: 'CA',
    },
  },
};

const { error, value } = schema.validate(data);

if (error) {
  console.log(error.message);
} else {
  console.log(value);
}
```

**20. Validating a Property as a Function**

```js
const schema = Joi.object().keys({
  greet: Joi.func().required(),
});

const data = {
  greet: function(name) {
    return `Hello, ${name}!`;
  },
};

const { error, value } = schema.validate(data);

if (error) {
  console.log(error.message);
} else {
  console.log(value);
}
```

**21. Validating a Property as a Boolean**

```js
const schema = Joi.object().keys({
  is_active: Joi.boolean().required(),
});

const data = {
  is_active: true,
};

const { error, value } = schema.validate(data);

if (error) {
  console.log(error.message);
} else {
  console.log(value);
}
```

**22. Validating a Property as a UUID**

```js
const schema = Joi.object().keys({
  user_id: Joi.string().guid({
    version: 'uuidv4',
  }).required(),
});

const data = {
  user_id: '936DA01F-9ABD-4D9D-80C7-02AF85C822A8',
};

const { error, value } = schema.validate(data);

if (error) {
  console.log(error.message);
} else {
  console.log(value);
}
```

**23. Validating a Property as an Array of Strings**

```js
const schema = Joi.object().keys({
  hobbies: Joi.array().items(Joi.string().required()),
});

const data = {
  hobbies: ['music', 'reading', 'sports'],
};

const { error, value } = schema.validate(data);

if (error) {
  console.log(error.message);
} else {
  console.log(value);
}
```

**24. Validating a Property as an Array of Objects**

```js
const schema = Joi.object().keys({
  pets: Joi.array().items(
    Joi.object().keys({
      name: Joi.string().required(),
      type: Joi.string().required(),
    })
  ),
});

const data = {
  pets: [
    { name: 'Max', type: 'dog' },
    { name: 'Luna', type: 'cat' },
  ],
};

const { error, value } = schema.validate(data);

if (error) {
  console.log(error.message);
} else {
  console.log(value);
}
```

**25. Validating a Property as a Date**

```js
const schema = Joi.object().keys({
  dob: Joi.date().required(),
});

const data = {
  dob: new Date('1990-01-01'),
};

const { error, value } = schema.validate(data);

if (error) {
  console.log(error.message);
} else {
  console.log(value);
}
```
