Skip to main content
PolyVal provides a comprehensive set of validation rules for different data types. Each rule is designed to be simple to use while covering common validation scenarios.

Common rules

These rules apply to all field types:

required

Makes a field mandatory. The field cannot be undefined, null, or an empty string.
const schema = {
  username: {
    type: 'string',
    required: true
  }
};
If a field is not marked as required and the value is undefined or null, all other validation rules are skipped for that field.

String rules

Validation rules specific to type: 'string' fields.

min

Minimum string length in characters.
const schema = {
  username: {
    type: 'string',
    min: 3  // Must be at least 3 characters
  }
};

max

Maximum string length in characters.
const schema = {
  username: {
    type: 'string',
    max: 20  // Must not exceed 20 characters
  }
};

length

Exact string length required.
const schema = {
  pin: {
    type: 'string',
    length: 6  // Must be exactly 6 characters
  }
};

email

Validates email format using a regex pattern.
const schema = {
  email: {
    type: 'string',
    email: true
  }
};
The email validation uses the pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/

regex

Validates against a custom regex pattern. Provide the pattern as a string.
const schema = {
  username: {
    type: 'string',
    regex: '^[a-zA-Z0-9]+$'  // Only alphanumeric characters
  },
  strongPassword: {
    type: 'string',
    regex: '^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&])[A-Za-z\\d@$!%*?&]+$'
  }
};
Provide regex patterns as strings, not RegExp objects. The string will be converted to a RegExp internally using new RegExp(pattern).
The regex rule is particularly useful for custom patterns like alphanumeric validation, phone numbers, or strong passwords.

Number rules

Validation rules specific to type: 'number' fields.

min

Minimum numeric value.
const schema = {
  age: {
    type: 'number',
    min: 18  // Must be at least 18
  }
};

max

Maximum numeric value.
const schema = {
  age: {
    type: 'number',
    max: 120  // Must not exceed 120
  }
};
Combine min and max to create a valid range: { type: 'number', min: 0, max: 100 }

Date rules

The type: 'date' field type is supported for type checking only. Date values must be JavaScript Date objects.
const schema = {
  appointmentDate: {
    type: 'date',
    required: true
  }
};

const data = {
  appointmentDate: new Date()
};
Currently, date fields support type validation only. Min/max date validation is not yet implemented in the current version.

Boolean rules

Validation rules specific to type: 'boolean' fields.

equals

For boolean fields, equals checks if the value matches a specific boolean.
const schema = {
  acceptTerms: {
    type: 'boolean',
    required: true,
    equals: true  // Must be true (e.g., checkbox must be checked)
  },
  optOut: {
    type: 'boolean',
    equals: false  // Must be false
  }
};

Field comparison rules

These rules compare a field’s value with another field in the data object.

equals

For non-boolean fields, equals compares the field value with another field.
const schema = {
  password: {
    type: 'string',
    required: true,
    min: 8
  },
  confirmPassword: {
    type: 'string',
    required: true,
    equals: 'password'  // Must match the 'password' field
  }
};
Implementation from source:
// From src/index.ts:269-303
if (fieldConfig.equals !== undefined) {
  if (typeof fieldConfig.equals === 'string') {
    const targetField = fieldConfig.equals;
    if (data[targetField] !== value) {
      let errorMessage: string;
      
      // Priority order
      if (customMessages?.fields?.[fieldName]?.equals) {
        // Field-based custom message
        const messageProvider = customMessages.fields[fieldName].equals;
        if (typeof messageProvider === 'function') {
          errorMessage = (messageProvider as (field: string) => string)(targetField);
        } else {
          errorMessage = messageProvider as string;
        }
      } else if (customMessages?.equals) {
        // General custom message
        errorMessage = customMessages.equals(targetField);
      } else {
        // Default message
        errorMessage = messages.equals(targetField);
      }
      
      errors.push(`${formattedFieldName}: ${errorMessage}`);
    }
  }
}

notEquals

Ensures the field value does NOT match another field.
const schema = {
  password: {
    type: 'string',
    required: true
  },
  oldPassword: {
    type: 'string',
    required: true,
    notEquals: 'password'  // Must NOT match the 'password' field
  }
};
Field comparison rules reference other fields by their key name in the data object. Make sure the referenced field exists.

Custom validators

Define your own validation logic with custom validators.

Structure

const schema = {
  username: {
    type: 'string',
    customValidators: [
      {
        validator: (value, data) => {
          // Return string for error, undefined for success
          if (value.toLowerCase() === 'admin') {
            return 'This username is not available';
          }
          return undefined;
        },
        messageKey: 'noAdminUsername'  // Optional
      }
    ]
  }
};

Validator function

The validator function receives two parameters:
  • value - The current field’s value
  • data - The entire data object being validated
Return:
  • string - Error message if validation fails
  • undefined - If validation passes

Multiple validators

You can add multiple custom validators to a single field:
const schema = {
  username: {
    type: 'string',
    customValidators: [
      {
        validator: (value) => {
          return value.toLowerCase() !== 'admin' 
            ? undefined 
            : 'Admin username not allowed';
        },
        messageKey: 'noAdminUsername'
      },
      {
        validator: (value) => {
          return !value.includes('_') 
            ? undefined 
            : 'Underscores not allowed';
        },
        messageKey: 'noUnderscores'
      }
    ]
  }
};

Accessing other fields

Use the data parameter to access other fields:
const schema = {
  confirmPassword: {
    type: 'string',
    customValidators: [
      {
        validator: (value, data) => {
          // Access the password field
          if (value === data.password + '123') {
            return 'Password is too predictable';
          }
          return undefined;
        },
        messageKey: 'predictablePassword'
      }
    ]
  }
};
Implementation from source:
// From src/index.ts:332-366
if (fieldConfig.customValidators && fieldConfig.customValidators.length > 0) {
  for (const validator of fieldConfig.customValidators) {
    const validationResult = validator.validator(value, data);
    
    if (validationResult !== undefined) {
      // Custom validator returned an error
      let errorMessage = validationResult;
      
      // If messageKey is specified, check custom messages
      if (validator.messageKey) {
        // Priority order
        if (customMessages?.fields?.[fieldName]?.[validator.messageKey]) {
          // Field-based custom message
          const messageProvider = customMessages.fields[fieldName][validator.messageKey];
          if (typeof messageProvider === 'function') {
            errorMessage = (messageProvider as (value: any, data: Record<string, any>) => string)(value, data);
          } else {
            errorMessage = messageProvider as string;
          }
        } else if (customMessages?.custom?.[validator.messageKey]) {
          // Global custom message
          const messageProvider = customMessages.custom[validator.messageKey];
          if (typeof messageProvider === 'function') {
            errorMessage = (messageProvider as (value: any, data: Record<string, any>) => string)(value, data);
          } else {
            errorMessage = messageProvider as string;
          }
        }
      }
      
      errors.push(`${formattedFieldName}: ${errorMessage}`);
    }
  }
}

Combining rules

You can combine multiple validation rules on a single field:
const schema = {
  username: {
    type: 'string',
    required: true,
    min: 3,
    max: 20,
    regex: '^[a-zA-Z0-9]+$',
    customValidators: [
      {
        validator: (value) => {
          return value.toLowerCase() !== 'admin' 
            ? undefined 
            : 'Reserved username';
        }
      }
    ]
  }
};
Validation rules are checked in order. If a field fails the type check, subsequent rules are skipped for that field.

Build docs developers (and LLMs) love