Fluent API
The Fluent API provides a chainable, type-safe way to define validation rules with excellent IDE support and autocompletion.
Overview
The Fluent API is the most powerful and developer-friendly way to define validation schemas in ValidlyJS. It provides:
- Type Safety: Full TypeScript support with compile-time type checking
- IDE Support: Excellent autocompletion and IntelliSense
- Chainable Methods: Intuitive method chaining for building complex rules
- Performance: Optimized for runtime performance
Note: The Fluent API is ideal for TypeScript projects and developers who prefer a programmatic, chainable interface for building validation schemas.
Basic Usage
import { Validator, string, number, boolean } from 'validlyjs';
const validator = new Validator({
name: string().required().min(3).max(50),
email: string().required().email(),
age: number().required().min(18).max(120),
isActive: boolean().required()
});
const result = await validator.validate({
name: 'John Doe',
email: 'john@example.com',
age: 25,
isActive: true
});
Available Builders
String Builder
import { string } from 'validlyjs';
// Basic string validation
string().required().min(3).max(100)
// Email validation
string().required().email()
// URL validation
string().required().url()
// Pattern matching
string().required().regex('/^[A-Z][a-z]+$/')
// Character type validation
string().required().alpha() // Only letters
string().required().alphaNum() // Letters and numbers
string().required().alphaNumDash() // Letters, numbers, dashes, underscores
// Content validation
string().required().startsWith('prefix')
string().required().endsWith('suffix')
string().required().contains('substring')
// Value constraints
string().required().in(['option1', 'option2', 'option3'])
string().required().notIn(['banned1', 'banned2'])
// Format validation
string().required().uuid()
string().required().json()
Number Builder
import { number } from 'validlyjs';
// Basic number validation
number().required().min(0).max(100)
// Integer validation
number().required().integer()
// Positive/negative validation
number().required().positive()
number().required().negative()
// Multiple validation
number().required().multipleOf(5)
// Range validation
number().required().between(10, 50)
// Value constraints
number().required().in([1, 2, 3, 5, 8])
number().required().notIn([13, 666])
Date Builder
import { date } from 'validlyjs';
// Basic date validation
date().required().after(new Date('2023-01-01'))
date().required().before(new Date('2024-12-31'))
// Format validation
date().required().format('YYYY-MM-DD')
Array Builder
import { array, string, number } from 'validlyjs';
// Basic array validation
array().required().min(1).max(10)
// Array of specific type
array(string().required().email()).required().min(1)
array(number().required().positive()).required().max(5)
// Unique elements
array(string()).required().unique()
// Contains validation
array(string()).required().contains('required-item')
Object Builder
import { object, string, number } from 'validlyjs';
// Nested object validation
const userSchema = object({
profile: object({
firstName: string().required().min(2),
lastName: string().required().min(2),
age: number().required().min(18)
}).required(),
preferences: object({
theme: string().required().in(['light', 'dark sm']).required(),
notifications: boolean().required()
}).optional()
}).required();
File Builder
import { file } from 'validlyjs';
// File validation
file().required()
.maxSize(5 * 1024 * 1024) // 5MB
.mimeTypes(['image/jpeg', 'image/png', 'image/gif'])
.extensions(['jpg', 'jpeg', 'png', 'gif'])
// Image-specific validation
file().required()
.image()
.maxDimensions(1920, 1080)
.minDimensions(100, 100)
Conditional Validation
import { Validator, string, number } from 'validlyjs';
const validator = new Validator({
type: string().required().in(['individual', 'company']),
// Required only if type is 'individual'
firstName: string().requiredIf('type', 'individual').min(2),
lastName: string().requiredIf('type', 'individual').min(2),
// Required only if type is 'company'
companyName: string().requiredIf('type', 'company').min(2),
// Required with other fields
password: string().required().min(8),
passwordConfirmation: string().requiredWith('password').same('password')
});
Union Types
import { union, string, number } from 'validlyjs';
// Value can be either string or number
const validator = new Validator({
value: union(
add(string().min(3))
.add(number().positive())
).required()
});
// More complex union
const validator2 = new Validator({
contact: union(
add(string().email()) // Email address
.add(string().regex('/^\+\d+$/')), // Phone number
.add(string().url()) // Website URL
).required()
});
Custom Rules
import { string } from 'validlyjs';
// Using custom rules with fluent API
const validator = new Validator({
username: string()
.required()
.min(3)
.max(20)
.custom('unique_username', async (value) => {
const exists = await checkUsernameExists(value);
return !exists;
}, 'Username is already taken')
});
Method Chaining Rules
When using the fluent API, the order of method calls generally doesn't matter, but there are some best practices:
// Recommended order: type → required/optional → constraints → format
string()
.required() // 1. Required/optional first
.min(3) // 2. Size constraints
.max(50) // 3. More constraints
.email() // 4. Format validation last
// All of these are equivalent:
string().required().min(3).email()
string().email().required().min(3)
string().min(3).email().required()
Tip: Following a consistent order improves readability and maintainability.
Performance Tips
Optimization Strategies
- Reuse Validators: Create validator instances once and reuse them
- Order Rules: Put faster rules (like required) before slower ones (like async custom rules)
- Use Specific Types: Use the most specific builder type for better performance
- Avoid Deep Nesting: Very deep object nesting can impact performance