Aphex
Schema Types

Validation

Validate field values with a fluent, chainable rule API.

Aphex provides a Sanity-style validation API. Each field can define validation rules using a Rule object passed to the validation callback.

Basic Usage

{
  name: 'title',
  type: 'string',
  title: 'Title',
  validation: (Rule) => Rule.required().max(100)
}

Rules are chainable -- each method returns a new Rule instance so you can combine multiple constraints:

validation: (Rule) => Rule.required().min(3).max(50);

Multiple Validators

Pass an array of rule functions to apply independent validation rules with different severity levels:

validation: [
	(Rule) => Rule.required(),
	(Rule) => Rule.max(100).warning('Consider keeping titles under 100 characters')
];

Severity Levels

By default, validation failures are errors. Use .warning() or .info() to change the severity:

// Error (blocks publishing) -- default
validation: (Rule) => Rule.required().error('Title is required');

// Warning (shows warning but allows publishing)
validation: (Rule) => Rule.max(60).warning('SEO titles should be under 60 characters');

// Info (informational hint)
validation: (Rule) => Rule.max(280).info('This will be truncated on mobile');

Custom Validation

The custom() method accepts a function that receives the field value and a context object:

validation: (Rule) =>
	Rule.custom((value, context) => {
		// Return true if valid
		if (!value) return true;

		// Return a string message if invalid
		if (value.startsWith('_')) {
			return 'Cannot start with an underscore';
		}

		// Return true or omit for valid
		return true;
	});

Context object

The custom validator receives a context with:

PropertyTypeDescription
documentobjectThe full document data.
parentobjectThe parent object containing this field.
pathstring[]The path to this field in the document.

Async validation

Custom validators can be async:

validation: (Rule) =>
	Rule.custom(async (value) => {
		const exists = await checkSlugExists(value);
		return exists ? 'This slug is already taken' : true;
	});

Cross-Field Validation

Use Rule.valueOfField() to reference another field's value in constraints:

{
  name: 'endDate',
  type: 'date',
  title: 'End Date',
  validation: (Rule) => Rule.custom((value, context) => {
    const startDate = context.parent?.startDate;
    if (startDate && value && value < startDate) {
      return 'End date must be after start date';
    }
    return true;
  })
}

All Available Rules

Common (all field types)

MethodDescription
required()Value must be provided. Empty strings, null, and undefined fail.
custom(fn)Custom validation function. See Custom Validation.
error(message?)Set severity to error (default). Optionally override the message.
warning(message?)Set severity to warning.
info(message?)Set severity to info.

String / Text / Slug

MethodDescription
min(length)Minimum character count.
max(length)Maximum character count.
length(exact)Exact character count.
email()Must be a valid email address.
regex(pattern, name?)Must match the regex pattern.

URL

MethodDescription
uri(options?)Must be a valid URI. Options: scheme (RegExp[]), allowRelative (boolean), relativeOnly (boolean).

Number

MethodDescription
min(value)Minimum numeric value.
max(value)Maximum numeric value.
positive()Must be greater than 0.
negative()Must be less than 0.
integer()Must be a whole number.
greaterThan(num)Must be strictly greater than num.
lessThan(num)Must be strictly less than num.

Array

MethodDescription
min(count)Minimum number of items.
max(count)Maximum number of items.
length(count)Exact number of items.
unique()All items must be unique. Compares values deeply, excluding _key properties.

Date / Datetime

MethodDescription
date(format?)Must be a valid date. Format defaults to the field's dateFormat option.
datetime(dateFormat?, timeFormat?)Must be a valid datetime. Formats default to the field's options.

Examples

Required string with length bounds

validation: (Rule) => Rule.required().min(3).max(100);

Positive integer

validation: (Rule) => Rule.required().integer().positive();

Unique array with min/max

validation: (Rule) => Rule.required().min(1).max(10).unique();

Conditional warning

validation: [
	(Rule) => Rule.required(),
	(Rule) => Rule.max(60).warning('Consider a shorter title for SEO')
];

Custom with document context

validation: (Rule) =>
	Rule.custom((value, context) => {
		if (context.document?.published && !value) {
			return 'This field is required for published documents';
		}
		return true;
	});
Edit on GitHub

Last updated on