Logo
⚠️ Unsaved
[M]:

Joi Cheatsheet: Quick Reference to Validation Rules

This notebook provides a comprehensive cheatsheet for Joi, the powerful schema validation library for JavaScript. Whether you're validating API requests, form inputs, or configuration objects, this quick reference guide will help you implement robust data validation in your Node.js applications.

[1]:
// Install Joi using npm
!npm install joi
$ npm install joi

added 6 packages in 2s

28 packages are looking for funding
  run `npm fund` for details
[2]:
// Import Joi
const Joi = require('joi');

// Check if Joi is loaded correctly
console.log("Joi version:", Joi.version, "\n");
Joi version: 17.13.3 
[M]:

1. String Validation Rules

Common validation rules for strings.

[3]:
// Basic string validation
const stringSchema = Joi.string(); // Any string

// Test basic string validation
console.log("Basic string validation:\n");
console.log(stringSchema.validate('hello'), "\n");
console.log(stringSchema.validate(123), "\n"); // Not a string
Basic string validation:
{ value: 'hello' } 
{
  value: 123,
  error: [Error [ValidationError]: "value" must be a string] {
    _original: 123,
    details: [ [Object] ]
  }
} 
[4]:
// String length validation
const lengthSchema = Joi.string()
.min(3) // Minimum length
.max(30) // Maximum length
.length(5); // Exact length (overrides min/max)

// Test string length validation
console.log("String length validation:\n");
console.log(lengthSchema.validate('hello'), "\n"); // Valid (length 5)
console.log(lengthSchema.validate('hi'), "\n"); // Invalid (too short)
String length validation:
{ value: 'hello' } 
{
  value: 'hi',
  error: [Error [ValidationError]: "value" length must be at least 3 characters long] {
    _original: 'hi',
    details: [ [Object] ]
  }
} 
[5]:
// String pattern validation
const patternSchema = Joi.string()
.pattern(/^[a-zA-Z0-9]{3,30}$/); // Alphanumeric, 3-30 chars

// Test pattern validation
console.log("String pattern validation:\n");
console.log(patternSchema.validate('abc123'), "\n"); // Valid
console.log(patternSchema.validate('abc-123'), "\n"); // Invalid (has hyphen)
String pattern validation:
{ value: 'abc123' } 
{
  value: 'abc-123',
  error: [Error [ValidationError]: "value" with value "abc-123" fails to match the required pattern: /^[a-zA-Z0-9]{3,30}$/] {
    _original: 'abc-123',
    details: [ [Object] ]
  }
} 
[6]:
// Common string formats
const emailSchema = Joi.string().email();
const uriSchema = Joi.string().uri();
const uuidSchema = Joi.string().guid({ version: 'uuidv4' });
const hexSchema = Joi.string().hex();
const alphanumSchema = Joi.string().alphanum();
const tokenSchema = Joi.string().token(); // Alphanumeric + underscore
const base64Schema = Joi.string().base64();
const hostnameSchema = Joi.string().hostname();
const ipSchema = Joi.string().ip();

// Test email validation
console.log("Email validation:\n");
console.log(emailSchema.validate('user@example.com'), "\n"); // Valid
console.log(emailSchema.validate('invalid-email'), "\n"); // Invalid
Email validation:
{ value: 'user@example.com' } 
{
  value: 'invalid-email',
  error: [Error [ValidationError]: "value" must be a valid email] {
    _original: 'invalid-email',
    details: [ [Object] ]
  }
} 
[7]:
// String case transformation
const lowerSchema = Joi.string().lowercase();
const upperSchema = Joi.string().uppercase();

// Test case transformation
console.log("String case transformation:\n");
console.log(lowerSchema.validate('HELLO'), "\n"); // Transforms to 'hello'
String case transformation:
{ value: 'hello' } 
[M]:

2. Number Validation Rules

Common validation rules for numbers.

[8]:
// Basic number validation
const numberSchema = Joi.number(); // Any number

// Test basic number validation
console.log("Basic number validation:\n");
console.log(numberSchema.validate(42), "\n"); // Valid
console.log(numberSchema.validate(3.14), "\n"); // Valid
console.log(numberSchema.validate('42'), "\n"); // Valid (converts string to number)
console.log(numberSchema.validate('abc'), "\n"); // Invalid
Basic number validation:
{ value: 42 } 
{ value: 3.14 } 
{ value: 42 } 
{
  value: 'abc',
  error: [Error [ValidationError]: "value" must be a number] {
    _original: 'abc',
    details: [ [Object] ]
  }
} 
[9]:
// Number range validation
const rangeSchema = Joi.number()
.min(0) // Minimum value (inclusive)
.max(100) // Maximum value (inclusive)
.greater(0) // Greater than (exclusive)
.less(100); // Less than (exclusive)

// Test number range validation
console.log("Number range validation:\n");
console.log(rangeSchema.validate(50), "\n"); // Valid
console.log(rangeSchema.validate(0), "\n"); // Invalid (must be greater than 0)
console.log(rangeSchema.validate(100), "\n"); // Invalid (must be less than 100)
Number range validation:
{ value: 50 } 
{
  value: 0,
  error: [Error [ValidationError]: "value" must be greater than 0] {
    _original: 0,
    details: [ [Object] ]
  }
} 
{
  value: 100,
  error: [Error [ValidationError]: "value" must be less than 100] {
    _original: 100,
    details: [ [Object] ]
  }
} 
[10]:
// Integer validation
const integerSchema = Joi.number().integer();

// Test integer validation
console.log("Integer validation:\n");
console.log(integerSchema.validate(42), "\n"); // Valid
console.log(integerSchema.validate(3.14), "\n"); // Invalid (not an integer)
Integer validation:
{ value: 42 } 
{
  value: 3.14,
  error: [Error [ValidationError]: "value" must be an integer] {
    _original: 3.14,
    details: [ [Object] ]
  }
} 
[11]:
// Precision and sign validation
const precisionSchema = Joi.number().precision(2); // Max 2 decimal places
const positiveSchema = Joi.number().positive(); // Must be positive
const negativeSchema = Joi.number().negative(); // Must be negative
const portSchema = Joi.number().port(); // Valid port number (0-65535)

// Test precision validation
console.log("Precision validation:\n");
console.log(precisionSchema.validate(3.14), "\n"); // Valid
console.log(precisionSchema.validate(3.142), "\n"); // Valid (rounds to 3.14)

// Test sign validation
console.log("Sign validation:\n");
console.log(positiveSchema.validate(42), "\n"); // Valid
console.log(positiveSchema.validate(-42), "\n"); // Invalid
Precision validation:
{ value: 3.14 } 
{ value: 3.14 } 
Sign validation:
{ value: 42 } 
{
  value: -42,
  error: [Error [ValidationError]: "value" must be a positive number] {
    _original: -42,
    details: [ [Object] ]
  }
} 
[M]:

3. Boolean Validation Rules

Common validation rules for booleans.

[12]:
// Basic boolean validation
const booleanSchema = Joi.boolean();

// Test basic boolean validation
console.log("Basic boolean validation:\n");
console.log(booleanSchema.validate(true), "\n"); // Valid
console.log(booleanSchema.validate(false), "\n"); // Valid
console.log(booleanSchema.validate('true'), "\n"); // Valid (converts to true)
console.log(booleanSchema.validate('yes'), "\n"); // Invalid
console.log(booleanSchema.validate(1), "\n"); // Invalid
console.log(booleanSchema.validate('abc'), "\n"); // Invalid
Basic boolean validation:
{ value: true } 
{ value: false } 
{ value: true } 
{
  value: 'yes',
  error: [Error [ValidationError]: "value" must be a boolean] {
    _original: 'yes',
    details: [ [Object] ]
  }
} 
{
  value: 1,
  error: [Error [ValidationError]: "value" must be a boolean] {
    _original: 1,
    details: [ [Object] ]
  }
} 
{
  value: 'abc',
  error: [Error [ValidationError]: "value" must be a boolean] {
    _original: 'abc',
    details: [ [Object] ]
  }
} 
[13]:
// Truthy/falsy values
const strictBooleanSchema = Joi.boolean().strict(); // No type conversion

// Test strict boolean validation
console.log("Strict boolean validation:\n");
console.log(strictBooleanSchema.validate(true), "\n"); // Valid
console.log(strictBooleanSchema.validate('true'), "\n"); // Invalid (string, not boolean)
Strict boolean validation:
{ value: true } 
{
  value: 'true',
  error: [Error [ValidationError]: "value" must be a boolean] {
    _original: 'true',
    details: [ [Object] ]
  }
} 
[14]:
// Specific boolean value
const trueBooleanSchema = Joi.boolean().valid(true); // Must be true

// Test specific boolean validation
console.log("Specific boolean validation:\n");
console.log(trueBooleanSchema.validate(true), "\n"); // Valid
console.log(trueBooleanSchema.validate(false), "\n"); // Invalid
Specific boolean validation:
{ value: true } 
{
  value: false,
  error: [Error [ValidationError]: "value" must be [true]] {
    _original: false,
    details: [ [Object] ]
  }
} 
[M]:

4. Date Validation Rules

Common validation rules for dates.

[15]:
// Basic date validation
const dateSchema = Joi.date();

// Test basic date validation
console.log("Basic date validation:\n");
console.log(dateSchema.validate(new Date()), "\n"); // Valid
console.log(dateSchema.validate('2023-01-01'), "\n"); // Valid (converts to Date)
console.log(dateSchema.validate(1672531200000), "\n"); // Valid (timestamp)
console.log(dateSchema.validate('not a date'), "\n"); // Invalid
Basic date validation:
{ value: 2025-03-12T19:24:13.464Z } 
{ value: 2023-01-01T00:00:00.000Z } 
{ value: 2023-01-01T00:00:00.000Z } 
{
  value: 'not a date',
  error: [Error [ValidationError]: "value" must be a valid date] {
    _original: 'not a date',
    details: [ [Object] ]
  }
} 
[16]:
// Date range validation
const now = new Date();
const yesterday = new Date(now.getTime() - 24 * 60 * 60 * 1000);
const tomorrow = new Date(now.getTime() + 24 * 60 * 60 * 1000);

const dateRangeSchema = Joi.date()
.min(yesterday) // Minimum date (inclusive)
.max(tomorrow); // Maximum date (inclusive)

// Test date range validation
console.log("Date range validation:\n");
console.log(dateRangeSchema.validate(now), "\n"); // Valid
console.log(dateRangeSchema.validate(new Date('2000-01-01')), "\n"); // Invalid (too old)
Date range validation:
{ value: 2025-03-12T19:24:15.901Z } 
{
  value: 2000-01-01T00:00:00.000Z,
  error: [Error [ValidationError]: "value" must be greater than or equal to "2025-03-11T19:24:15.901Z"] {
    _original: 2000-01-01T00:00:00.000Z,
    details: [ [Object] ]
  }
} 
[17]:
// Relative date validation
const relativeDateSchema = Joi.date()
.greater('now') // Must be in the future
.less('1-1-2100'); // Must be before 2100

// Test relative date validation
console.log("Relative date validation:\n");
console.log(relativeDateSchema.validate(tomorrow), "\n"); // Valid
console.log(relativeDateSchema.validate(yesterday), "\n"); // Invalid (in the past)
Relative date validation:
{ value: 2025-03-13T19:24:15.901Z } 
{
  value: 2025-03-11T19:24:15.901Z,
  error: [Error [ValidationError]: "value" must be greater than "now"] {
    _original: 2025-03-11T19:24:15.901Z,
    details: [ [Object] ]
  }
} 
[18]:
// ISO date format validation
const isoDateSchema = Joi.date().iso();

// Test ISO date validation
console.log("ISO date validation:\n");
console.log(isoDateSchema.validate('2023-01-01T00:00:00.000Z'), "\n"); // Valid
console.log(isoDateSchema.validate('01/01/2023'), "\n"); // Invalid (not ISO format)
ISO date validation:
{ value: 2023-01-01T00:00:00.000Z } 
{
  value: '01/01/2023',
  error: [Error [ValidationError]: "value" must be in ISO 8601 date format] {
    _original: '01/01/2023',
    details: [ [Object] ]
  }
} 
[M]:

5. Array Validation Rules

Common validation rules for arrays.

[19]:
// Basic array validation
const arraySchema = Joi.array();

// Test basic array validation
console.log("Basic array validation:\n");
console.log(arraySchema.validate([1, 2, 3]), "\n"); // Valid
console.log(arraySchema.validate('not an array'), "\n"); // Invalid
Basic array validation:
{ value: [ 1, 2, 3 ] } 
{
  value: undefined,
  error: [Error [ValidationError]: "value" must be an array] {
    _original: 'not an array',
    details: [ [Object] ]
  }
} 
[20]:
// Array items validation
const stringArraySchema = Joi.array().items(Joi.string());
const numberArraySchema = Joi.array().items(Joi.number());
const mixedArraySchema = Joi.array().items(Joi.string(), Joi.number());

// Test array items validation
console.log("Array items validation:\n");
console.log(stringArraySchema.validate(['a', 'b', 'c']), "\n"); // Valid
console.log(stringArraySchema.validate(['a', 1, 'c']), "\n"); // Invalid (contains number)
console.log(mixedArraySchema.validate(['a', 1, 'c']), "\n"); // Valid (allows strings and numbers)
Array items validation:
{ value: [ 'a', 'b', 'c' ] } 
{
  value: [ 'a', 1, 'c' ],
  error: [Error [ValidationError]: "[1]" must be a string] {
    _original: [ 'a', 1, 'c' ],
    details: [ [Object] ]
  }
} 
{ value: [ 'a', 1, 'c' ] } 
[21]:
// Array length validation
const arrayLengthSchema = Joi.array()
.min(1) // Minimum length
.max(5) // Maximum length
.length(3); // Exact length (overrides min/max)

// Test array length validation
console.log("Array length validation:\n");
console.log(arrayLengthSchema.validate([1, 2, 3]), "\n"); // Valid (length 3)
console.log(arrayLengthSchema.validate([1, 2]), "\n"); // Invalid (too short)
Array length validation:
{ value: [ 1, 2, 3 ] } 
{
  value: [ 1, 2 ],
  error: [Error [ValidationError]: "value" must contain 3 items] {
    _original: [ 1, 2 ],
    details: [ [Object] ]
  }
} 
[22]:
// Unique array items
const uniqueArraySchema = Joi.array().items(Joi.string()).unique();

// Test unique array validation
console.log("Unique array validation:\n");
console.log(uniqueArraySchema.validate(['a', 'b', 'c']), "\n"); // Valid
console.log(uniqueArraySchema.validate(['a', 'b', 'a']), "\n"); // Invalid (duplicate 'a')
Unique array validation:
{ value: [ 'a', 'b', 'c' ] } 
{
  value: [ 'a', 'b', 'a' ],
  error: [Error [ValidationError]: "[2]" contains a duplicate value] {
    _original: [ 'a', 'b', 'a' ],
    details: [ [Object] ]
  }
} 
[23]:
// Ordered array items
const orderedArraySchema = Joi.array().ordered(
Joi.string().required(),
Joi.number().required(),
Joi.boolean().required()
);

// Test ordered array validation
console.log("Ordered array validation:\n");
console.log(orderedArraySchema.validate(['a', 1, true]), "\n"); // Valid
console.log(orderedArraySchema.validate([1, 'a', true]), "\n"); // Invalid (wrong order)
Ordered array validation:
{ value: [ 'a', 1, true ] } 
{
  value: [ 1, 'a', true ],
  error: [Error [ValidationError]: "[0]" must be a string] {
    _original: [ 1, 'a', true ],
    details: [ [Object] ]
  }
} 
[M]:

6. Object Validation Rules

Common validation rules for objects.

[24]:
// Basic object validation
const objectSchema = Joi.object();

// Test basic object validation
console.log("Basic object validation:\n");
console.log(objectSchema.validate({ key: 'value' }), "\n"); // Valid
console.log(objectSchema.validate('not an object'), "\n"); // Invalid
Basic object validation:
{ value: { key: 'value' } } 
{
  value: 'not an object',
  error: [Error [ValidationError]: "value" must be of type object] {
    _original: 'not an object',
    details: [ [Object] ]
  }
} 
[25]:
// Object with defined keys
const userSchema = Joi.object({
username: Joi.string().alphanum().min(3).max(30).required(),
email: Joi.string().email().required(),
age: Joi.number().integer().min(18).required(),
created: Joi.date().default(Date.now)
});

// Test object with keys validation
console.log("Object with keys validation:\n");
console.log(userSchema.validate({
username: 'johndoe',
email: 'john@example.com',
age: 25
}), "\n"); // Valid (created will be added with default value)
Object with keys validation:
{
  value: {
    username: 'johndoe',
    email: 'john@example.com',
    age: 25,
    created: 1741807477463
  }
} 
[26]:
// Unknown keys handling
const strictSchema = Joi.object({
name: Joi.string().required(),
age: Joi.number().required()
}).unknown(false); // Reject unknown keys

const flexibleSchema = Joi.object({
name: Joi.string().required(),
age: Joi.number().required()
}).unknown(true); // Allow unknown keys

// Test unknown keys handling
console.log("Unknown keys handling:\n");
const dataWithExtra = {
name: 'John',
age: 30,
extra: 'field'
};

console.log("Strict schema:", strictSchema.validate(dataWithExtra), "\n");
console.log("Flexible schema:", flexibleSchema.validate(dataWithExtra), "\n");
Unknown keys handling:
Strict schema: {
  value: { name: 'John', age: 30, extra: 'field' },
  error: [Error [ValidationError]: "extra" is not allowed] {
    _original: { name: 'John', age: 30, extra: 'field' },
    details: [ [Object] ]
  }
} 
Flexible schema: { value: { name: 'John', age: 30, extra: 'field' } } 
[27]:
// Required and optional keys
const addressSchema = Joi.object({
street: Joi.string().required(),
city: Joi.string().required(),
state: Joi.string().length(2).required(),
zip: Joi.string().pattern(/^\d{5}$/).required(),
apartment: Joi.string().optional(),
country: Joi.string().default('USA')
});

// Test required and optional keys
console.log("Required and optional keys:\n");
console.log(addressSchema.validate({
street: '123 Main St',
city: 'Anytown',
state: 'CA',
zip: '12345'
}), "\n"); // Valid (apartment is optional, country has default)
Required and optional keys:
{
  value: {
    street: '123 Main St',
    city: 'Anytown',
    state: 'CA',
    zip: '12345',
    country: 'USA'
  }
} 
[28]:
// Nested objects
const profileSchema = Joi.object({
user: Joi.object({
name: Joi.string().required(),
email: Joi.string().email().required()
}).required(),
preferences: Joi.object({
theme: Joi.string().valid('light', 'dark').default('light'),
notifications: Joi.boolean().default(true)
}).optional()
});

// Test nested objects
console.log("Nested objects validation:\n");
console.log(profileSchema.validate({
user: {
name: 'John Doe',
email: 'john@example.com'
},
preferences: {
theme: 'dark'
}
}), "\n"); // Valid
Nested objects validation:
{
  value: {
    user: { name: 'John Doe', email: 'john@example.com' },
    preferences: { theme: 'dark', notifications: true }
  }
} 
[M]:

7. Logical Operators

Combining schemas with logical operators.

[29]:
// Alternatives (OR)
const identifierSchema = Joi.alternatives().try(
Joi.string().email(),
Joi.string().guid({ version: 'uuidv4' }),
Joi.string().pattern(/^[A-Z]{2}\d{6}$/)
);

// Test alternatives
console.log("Alternatives (OR) validation:\n");
console.log(identifierSchema.validate('user@example.com'), "\n"); // Valid (email)
console.log(identifierSchema.validate('123e4567-e89b-12d3-a456-426614174000'), "\n"); // Valid (UUID)
console.log(identifierSchema.validate('AB123456'), "\n"); // Valid (pattern)
console.log(identifierSchema.validate('invalid'), "\n"); // Invalid
Alternatives (OR) validation:
{ value: 'user@example.com' } 
{
  value: undefined,
  error: [Error [ValidationError]: "value" does not match any of the allowed types] {
    _original: '123e4567-e89b-12d3-a456-426614174000',
    details: [ [Object] ]
  }
} 
{ value: 'AB123456' } 
{
  value: undefined,
  error: [Error [ValidationError]: "value" does not match any of the allowed types] {
    _original: 'invalid',
    details: [ [Object] ]
  }
} 
[30]:
// Conditional validation
const paymentSchema = Joi.object({
paymentMethod: Joi.string().valid('credit', 'debit', 'paypal').required(),
// Credit card details (required if paymentMethod is credit or debit)
cardNumber: Joi.string().when('paymentMethod', {
is: Joi.valid('credit', 'debit'),
then: Joi.string().pattern(/^\d{16}$/).required(),
otherwise: Joi.forbidden()
}),
// PayPal email (required if paymentMethod is paypal)
paypalEmail: Joi.string().when('paymentMethod', {
is: 'paypal',
then: Joi.string().email().required(),
otherwise: Joi.forbidden()
})
});

// Test conditional validation
console.log("Conditional validation:\n");

// Valid credit card payment
console.log(paymentSchema.validate({
paymentMethod: 'credit',
cardNumber: '1234567890123456'
}), "\n");

// Valid PayPal payment
console.log(paymentSchema.validate({
paymentMethod: 'paypal',
paypalEmail: 'user@example.com'
}), "\n");

// Invalid (mixing payment methods)
console.log(paymentSchema.validate({
Conditional validation:
{ value: { paymentMethod: 'credit', cardNumber: '1234567890123456' } } 
{ value: { paymentMethod: 'paypal', paypalEmail: 'user@example.com' } } 
{
  value: { paymentMethod: 'credit', paypalEmail: 'user@example.com' },
  error: [Error [ValidationError]: "cardNumber" is required] {
    _original: { paymentMethod: 'credit', paypalEmail: 'user@example.com' },
    details: [ [Object] ]
  }
} 
[31]:
// Logical AND
const passwordSchema = Joi.string()
.min(8)
.max(30)
.pattern(/[a-z]/) // Must contain lowercase
.pattern(/[A-Z]/) // Must contain uppercase
.pattern(/\d/) // Must contain digit
.pattern(/[!@#$%^&*]/) // Must contain special char
.required();

// Test logical AND
console.log("Logical AND validation:\n");
console.log(passwordSchema.validate('Password123!'), "\n"); // Valid
console.log(passwordSchema.validate('password123'), "\n"); // Invalid (no uppercase)
console.log(passwordSchema.validate('Password'), "\n"); // Invalid (no digit or special char)
Logical AND validation:
{ value: 'Password123!' } 
{
  value: 'password123',
  error: [Error [ValidationError]: "value" with value "password123" fails to match the required pattern: /[A-Z]/] {
    _original: 'password123',
    details: [ [Object] ]
  }
} 
{
  value: 'Password',
  error: [Error [ValidationError]: "value" with value "Password" fails to match the required pattern: /\d/] {
    _original: 'Password',
    details: [ [Object] ]
  }
} 
[32]:
// Logical OR for object keys
const contactSchema = Joi.object({
name: Joi.string().required(),
email: Joi.string().email(),
phone: Joi.string().pattern(/^\d{10}$/)
}).or('email', 'phone'); // Either email or phone must be provided

// Test logical OR for keys
console.log("Logical OR for keys:\n");
console.log(contactSchema.validate({ name: 'John', email: 'john@example.com' }), "\n"); // Valid
console.log(contactSchema.validate({ name: 'John', phone: '1234567890' }), "\n"); // Valid
console.log(contactSchema.validate({ name: 'John' }), "\n"); // Invalid (missing email or phone)
Logical OR for keys:
{ value: { name: 'John', email: 'john@example.com' } } 
{ value: { name: 'John', phone: '1234567890' } } 
{
  value: { name: 'John' },
  error: [Error [ValidationError]: "value" must contain at least one of [email, phone]] {
    _original: { name: 'John' },
    details: [ [Object] ]
  }
} 
[33]:
// Logical XOR (exclusive OR)
const deliverySchema = Joi.object({
pickupLocation: Joi.string(),
deliveryAddress: Joi.string()
}).xor('pickupLocation', 'deliveryAddress'); // Either pickup or delivery, but not both

// Test logical XOR
console.log("Logical XOR validation:\n");
console.log(deliverySchema.validate({ pickupLocation: 'Store #123' }), "\n"); // Valid
console.log(deliverySchema.validate({ deliveryAddress: '123 Main St' }), "\n"); // Valid
console.log(deliverySchema.validate({ pickupLocation: 'Store #123', deliveryAddress: '123 Main St' }), "\n"); // Invalid (both provided)
console.log(deliverySchema.validate({}), "\n"); // Invalid (neither provided)
Logical XOR validation:
{ value: { pickupLocation: 'Store #123' } } 
{ value: { deliveryAddress: '123 Main St' } } 
{
  value: { pickupLocation: 'Store #123', deliveryAddress: '123 Main St' },
  error: [Error [ValidationError]: "value" contains a conflict between exclusive peers [pickupLocation, deliveryAddress]] {
    _original: { pickupLocation: 'Store #123', deliveryAddress: '123 Main St' },
    details: [ [Object] ]
  }
} 
{
  value: {},
  error: [Error [ValidationError]: "value" must contain at least one of [pickupLocation, deliveryAddress]] {
    _original: {},
    details: [ [Object] ]
  }
} 
[M]:

8. Validation Options

Common options for customizing validation behavior.

[34]:
// Basic validation options
const userSchema = Joi.object({
username: Joi.string().alphanum().min(3).max(30).required(),
email: Joi.string().email().required(),
age: Joi.number().integer().min(18).required(),
newsletter: Joi.boolean().default(false)
});

const userData = {
username: 'johndoe',
email: 'john@example.com',
age: '25', // String instead of number
extra: 'field' // Unknown field
};
[35]:
// Default validation (strict)
console.log("Default validation (strict):\n");
console.log(userSchema.validate(userData), "\n");
Default validation (strict):
{
  value: {
    username: 'johndoe',
    email: 'john@example.com',
    age: 25,
    extra: 'field',
    newsletter: false
  },
  error: [Error [ValidationError]: "extra" is not allowed] {
    _original: {
      username: 'johndoe',
      email: 'john@example.com',
      age: '25',
      extra: 'field'
    },
    details: [ [Object] ]
  }
} 
[36]:
// With type conversion
console.log("Validation with type conversion:\n");
console.log(userSchema.validate(userData, { convert: true }), "\n");
Validation with type conversion:
{
  value: {
    username: 'johndoe',
    email: 'john@example.com',
    age: 25,
    extra: 'field',
    newsletter: false
  },
  error: [Error [ValidationError]: "extra" is not allowed] {
    _original: {
      username: 'johndoe',
      email: 'john@example.com',
      age: '25',
      extra: 'field'
    },
    details: [ [Object] ]
  }
} 
[37]:
// Allow unknown keys
console.log("Validation allowing unknown keys:\n");
console.log(userSchema.validate(userData, { allowUnknown: true }), "\n");
Validation allowing unknown keys:
{
  value: {
    username: 'johndoe',
    email: 'john@example.com',
    age: 25,
    extra: 'field',
    newsletter: false
  }
} 
[38]:
// Strip unknown keys
console.log("Validation stripping unknown keys:\n");
console.log(userSchema.validate(userData, { stripUnknown: true }), "\n");
Validation stripping unknown keys:
{
  value: {
    username: 'johndoe',
    email: 'john@example.com',
    age: 25,
    newsletter: false
  }
} 
[39]:
// Abort early (stop on first error)
const invalidData = {
username: 'j', // Too short
email: 'not-an-email', // Invalid email
age: 16 // Below minimum
};

console.log("Validation with abort early (default):\n");
console.log(userSchema.validate(invalidData), "\n");

console.log("Validation with all errors:\n");
console.log(userSchema.validate(invalidData, { abortEarly: false }), "\n");
Validation with abort early (default):
{
  value: { username: 'j', email: 'not-an-email', age: 16 },
  error: [Error [ValidationError]: "username" length must be at least 3 characters long] {
    _original: { username: 'j', email: 'not-an-email', age: 16 },
    details: [ [Object] ]
  }
} 
Validation with all errors:
{
  value: { username: 'j', email: 'not-an-email', age: 16, newsletter: false },
  error: [Error [ValidationError]: "username" length must be at least 3 characters long. "email" must be a valid email. "age" must be greater than or equal to 18] {
    _original: { username: 'j', email: 'not-an-email', age: 16 },
    details: [ [Object], [Object], [Object] ]
  }
} 
[M]:

9. Custom Error Messages

Customizing validation error messages.

[40]:
// Custom error messages using .messages()
const registrationSchema = Joi.object({
username: Joi.string().alphanum().min(3).max(30).required()
.messages({
'string.base': 'Username must be a text value',
'string.empty': 'Username cannot be empty',
'string.min': 'Username must be at least {#limit} characters',
'string.max': 'Username cannot exceed {#limit} characters',
'string.alphanum': 'Username must only contain alphanumeric characters',
'any.required': 'Username is required'
}),
email: Joi.string().email().required()
.messages({
'string.email': 'Please enter a valid email address',
'any.required': 'Email is required'
}),
password: Joi.string().min(8).pattern(/[a-z]/).pattern(/[A-Z]/).pattern(/\d/).required()
.messages({
'string.min': 'Password must be at least {#limit} characters',
'string.pattern.base': 'Password must include lowercase, uppercase, and numbers',
'any.required': 'Password is required'
})
});
[41]:
// Test custom error messages
const invalidRegistration = {
username: 'a', // Too short
email: 'invalid-email', // Invalid email
password: 'password' // Missing uppercase and numbers
};

console.log("Custom error messages:\n");
const { error } = registrationSchema.validate(invalidRegistration, { abortEarly: false });
if (error) {
console.log("Validation errors:\n");
error.details.forEach(detail => {
console.log(`- ${detail.message}\n`);
});
}
Custom error messages:
Validation errors:
- Username must be at least 3 characters
- Please enter a valid email address
- Password must include lowercase, uppercase, and numbers
- Password must include lowercase, uppercase, and numbers
[42]:
// Global custom messages
const customJoi = Joi.defaults((schema) => {
return schema.error((errors) => {
return errors.map(error => {
// Custom messages for specific error types
if (error.code === 'string.min') {
error.message = `The field '${error.path.join('.')}' must be at least ${error.local.limit} characters`;
}
if (error.code === 'string.email') {
error.message = `The field '${error.path.join('.')}' must be a valid email address`;
}
return error;
});
});
});

// Create schema with custom Joi instance
const customSchema = customJoi.object({
name: customJoi.string().min(3).required(),
email: customJoi.string().email().required()
});

// Test global custom messages
console.log("Global custom messages:\n");
console.log(customSchema.validate({ name: 'a', email: 'not-email' }), "\n");
Global custom messages:
{
  value: { name: 'a', email: 'not-email' },
  error: [Error [ValidationError]: The field 'name' must be at least 3 characters] {
    _original: { name: 'a', email: 'not-email' },
    details: [ [Object] ]
  }
} 
[M]:

10. Practical Examples

Common real-world validation scenarios.

[43]:
// API request validation
const createUserRequestSchema = Joi.object({
// Request body
body: Joi.object({
name: Joi.string().min(2).max(50).required(),
email: Joi.string().email().required(),
role: Joi.string().valid('user', 'admin').default('user'),
settings: Joi.object({
theme: Joi.string().valid('light', 'dark').default('light'),
notifications: Joi.boolean().default(true)
}).default({})
}).required(),
// Query parameters
query: Joi.object({
sendWelcomeEmail: Joi.boolean().default(true)
}).default({}),
// Headers
headers: Joi.object({
'content-type': Joi.string().valid('application/json').required(),
'authorization': Joi.string().pattern(/^Bearer /).required()
}).unknown(true).required()
});

// Test API request validation
const apiRequest = {
body: {
name: 'John Doe',
email: 'john@example.com'
},
query: {
sendWelcomeEmail: false
},
headers: {
'content-type': 'application/json',
API request validation:
{
  value: {
    body: {
      name: 'John Doe',
      email: 'john@example.com',
      role: 'user',
      settings: {}
    },
    query: { sendWelcomeEmail: false },
    headers: {
      'content-type': 'application/json',
      authorization: 'Bearer token123',
      'user-agent': 'Mozilla/5.0'
    }
  }
} 
[44]:
// Form validation
const contactFormSchema = Joi.object({
firstName: Joi.string().min(2).max(50).required(),
lastName: Joi.string().min(2).max(50).required(),
email: Joi.string().email().required(),
phone: Joi.string().pattern(/^\d{10}$/).optional(),
subject: Joi.string().min(5).max(100).required(),
message: Joi.string().min(10).max(1000).required(),
subscribe: Joi.boolean().default(false)
});

// Test form validation
const formData = {
firstName: 'John',
lastName: 'Doe',
email: 'john@example.com',
subject: 'Question about your services',
message: 'I would like to know more about your web development services. Please contact me at your earliest convenience.',
subscribe: true
};

console.log("Form validation:\n");
console.log(contactFormSchema.validate(formData), "\n");
Form validation:
{
  value: {
    firstName: 'John',
    lastName: 'Doe',
    email: 'john@example.com',
    subject: 'Question about your services',
    message: 'I would like to know more about your web development services. Please contact me at your earliest convenience.',
    subscribe: true
  }
} 
[45]:
// Configuration validation
const configSchema = Joi.object({
app: Joi.object({
name: Joi.string().required(),
port: Joi.number().port().default(3000),
environment: Joi.string().valid('development', 'testing', 'production').default('development'),
debug: Joi.boolean().default(false)
}).required(),
database: Joi.object({
host: Joi.string().hostname().default('localhost'),
port: Joi.number().port().default(5432),
username: Joi.string().required(),
password: Joi.string().required(),
name: Joi.string().required(),
pool: Joi.object({
min: Joi.number().integer().min(1).default(2),
max: Joi.number().integer().min(2).default(10)
}).default()
}).required(),
logging: Joi.object({
level: Joi.string().valid('error', 'warn', 'info', 'debug').default('info'),
file: Joi.string().optional()
}).default()
});

// Test configuration validation
const config = {
app: {
name: 'MyApp',
port: 8080,
environment: 'production'
},
database: {
host: 'db.example.com',
Configuration validation:
{
  value: {
    app: {
      name: 'MyApp',
      port: 8080,
      environment: 'production',
      debug: false
    },
    database: {
      host: 'db.example.com',
      username: 'dbuser',
      password: 'dbpass',
      name: 'myapp_db',
      port: 5432,
      pool: [Object]
    },
    logging: { level: 'info' }
  }
} 
[M]:

Summary

This cheatsheet has covered the most common Joi validation rules and techniques:

  1. String Validation: Length, patterns, formats, and transformations
  2. Number Validation: Ranges, integers, precision, and sign
  3. Boolean Validation: Truthy/falsy values and conversions
  4. Date Validation: Ranges, formats, and relative dates
  5. Array Validation: Length, item types, and uniqueness
  6. Object Validation: Required/optional keys, defaults, and nesting
  7. Logical Operators: Alternatives, conditionals, and key requirements
  8. Validation Options: Type conversion, unknown keys, and error handling
  9. Custom Error Messages: Improving user experience with clear messages
  10. Practical Examples: Real-world validation scenarios

Joi provides a powerful, flexible system for data validation in JavaScript applications. By leveraging these validation rules, you can ensure data integrity, improve error handling, and create more robust applications.

For more information, check out the Joi documentation.

Sign in to save your work and access it from anywhere