Multi-Step Validation
Validate data through multiple stages.Copy
import { createRuleEngine, createRuleHelpers } from 'rule-engine-js';
const engine = createRuleEngine();
const rules = createRuleHelpers();
// Step 1: Basic validation
const basicValidation = rules.and(
rules.isNotNull('email'),
rules.isNotNull('password'),
rules.isNotNull('username')
);
// Step 2: Format validation
const formatValidation = rules.and(
rules.regex('email', '^[\\w\\.-]+@[\\w\\.-]+\\.[a-zA-Z]{2,}$'),
rules.gte('password.length', 8),
rules.gte('username.length', 3)
);
// Step 3: Business rules
const businessValidation = rules.and(
rules.gte('age', 18),
rules.notIn('username', 'bannedUsernames')
);
// Combine all steps
const fullValidation = rules.and(
basicValidation,
formatValidation,
businessValidation
);
const userData = {
email: 'user@example.com',
password: 'securepass123',
username: 'johndoe',
age: 25,
bannedUsernames: ['admin', 'root']
};
const result = engine.evaluateExpr(fullValidation, userData);
// { success: true }
Conditional Validation
Validate different fields based on conditions.Copy
// Validate shipping address only if not pickup
const shippingValidation = {
or: [
{ eq: ['deliveryMethod', 'pickup'] },
{
and: [
{ eq: ['deliveryMethod', 'shipping'] },
{ isNotNull: ['shippingAddress'] },
{ isNotNull: ['shippingAddress.street'] },
{ isNotNull: ['shippingAddress.city'] },
{ isNotNull: ['shippingAddress.zip'] }
]
}
]
};
// Pickup order - no address needed
const pickupOrder = {
deliveryMethod: 'pickup'
};
engine.evaluateExpr(shippingValidation, pickupOrder);
// { success: true }
// Shipping order - address required
const shippingOrder = {
deliveryMethod: 'shipping',
shippingAddress: {
street: '123 Main St',
city: 'New York',
zip: '10001'
}
};
engine.evaluateExpr(shippingValidation, shippingOrder);
// { success: true }
Payment Validation
Copy
const paymentValidation = {
and: [
// Has payment method
{ isNotNull: ['payment.method'] },
// Card validation
{
or: [
{ neq: ['payment.method', 'card'] },
{
and: [
{ eq: ['payment.method', 'card'] },
{ isNotNull: ['payment.cardNumber'] },
{ regex: ['payment.cardNumber', '^\\d{16}$'] },
{ isNotNull: ['payment.cvv'] },
{ regex: ['payment.cvv', '^\\d{3,4}$'] },
{ isNotNull: ['payment.expiryMonth'] },
{ between: ['payment.expiryMonth', [1, 12]] },
{ isNotNull: ['payment.expiryYear'] },
{ gte: ['payment.expiryYear', 2024] }
]
}
]
},
// PayPal validation
{
or: [
{ neq: ['payment.method', 'paypal'] },
{
and: [
{ eq: ['payment.method', 'paypal'] },
{ isNotNull: ['payment.paypalEmail'] },
{ regex: ['payment.paypalEmail', '^[\\w\\.-]+@[\\w\\.-]+\\.[a-zA-Z]{2,}$'] }
]
}
]
}
]
};
// Card payment
const cardPayment = {
payment: {
method: 'card',
cardNumber: '1234567890123456',
cvv: '123',
expiryMonth: 12,
expiryYear: 2025
}
};
engine.evaluateExpr(paymentValidation, cardPayment);
// { success: true }
// PayPal payment
const paypalPayment = {
payment: {
method: 'paypal',
paypalEmail: 'user@example.com'
}
};
engine.evaluateExpr(paymentValidation, paypalPayment);
// { success: true }
Cross-Field Validation
Validate relationships between fields.Copy
// Password confirmation
const passwordMatch = {
and: [
{ isNotNull: ['password'] },
{ isNotNull: ['confirmPassword'] },
{ eq: ['password', 'confirmPassword'] }
]
};
// Date range validation
const dateRange = {
and: [
{ isNotNull: ['startDate'] },
{ isNotNull: ['endDate'] },
{ lte: ['startDate', 'endDate'] }
]
};
// Discount validation
const discountValidation = {
and: [
{ gte: ['discount', 0] },
{ lt: ['discount', 'originalPrice'] },
{ gt: ['originalPrice', 0] }
]
};
const product = {
originalPrice: 100,
discount: 20
};
engine.evaluateExpr(discountValidation, product);
// { success: true }
Nested Object Validation
Copy
const userProfileValidation = {
and: [
// User basic info
{ isNotNull: ['user.name'] },
{ isNotNull: ['user.email'] },
{ gte: ['user.age', 18] },
// Address
{ isNotNull: ['user.address.street'] },
{ isNotNull: ['user.address.city'] },
{ regex: ['user.address.zip', '^\\d{5}$'] },
// Preferences
{ in: ['user.preferences.language', ['en', 'es', 'fr']] },
{ in: ['user.preferences.theme', ['light', 'dark']] },
// Permissions
{ isNotNull: ['user.permissions'] },
{ gte: ['user.permissions.length', 1] }
]
};
const userProfile = {
user: {
name: 'John Doe',
email: 'john@example.com',
age: 30,
address: {
street: '123 Main St',
city: 'New York',
zip: '10001'
},
preferences: {
language: 'en',
theme: 'dark'
},
permissions: ['read', 'write']
}
};
engine.evaluateExpr(userProfileValidation, userProfile);
// { success: true }
Array Validation
Copy
// Validate array contents
const cartValidation = {
and: [
// Has items
{ isNotNull: ['cart.items'] },
{ gt: ['cart.items.length', 0] },
{ lte: ['cart.items.length', 50] },
// Total is valid
{ gte: ['cart.total', 0] },
{ isNotNull: ['cart.currency'] }
]
};
// Order items validation
const orderValidation = {
and: [
{ gt: ['items.length', 0] },
{ gte: ['total', 0.01] },
{ eq: ['total', 'expectedTotal'] }
]
};
const order = {
items: [
{ id: 1, price: 29.99, quantity: 2 },
{ id: 2, price: 19.99, quantity: 1 }
],
total: 79.97,
expectedTotal: 79.97
};
engine.evaluateExpr(orderValidation, order);
// { success: true }
File Upload Validation
Copy
const fileValidation = {
and: [
// File exists
{ isNotNull: ['file.name'] },
{ isNotNull: ['file.size'] },
{ isNotNull: ['file.type'] },
// Size limits (10MB max)
{ gt: ['file.size', 0] },
{ lte: ['file.size', 10485760] },
// Allowed types
{
or: [
{ endsWith: ['file.name', '.jpg'] },
{ endsWith: ['file.name', '.png'] },
{ endsWith: ['file.name', '.pdf'] }
]
},
// MIME type check
{
in: ['file.type', [
'image/jpeg',
'image/png',
'application/pdf'
]]
}
]
};
const validFile = {
file: {
name: 'document.pdf',
size: 1048576, // 1MB
type: 'application/pdf'
}
};
engine.evaluateExpr(fileValidation, validFile);
// { success: true }
Form Wizard Validation
Multi-page form with step-by-step validation.Copy
// Step 1: Personal Info
const step1Validation = rules.and(
rules.isNotNull('firstName'),
rules.isNotNull('lastName'),
rules.isNotNull('email'),
rules.regex('email', '^[\\w\\.-]+@[\\w\\.-]+\\.[a-zA-Z]{2,}$')
);
// Step 2: Address
const step2Validation = rules.and(
rules.isNotNull('street'),
rules.isNotNull('city'),
rules.isNotNull('state'),
rules.regex('zip', '^\\d{5}$')
);
// Step 3: Payment
const step3Validation = rules.and(
rules.isNotNull('cardNumber'),
rules.regex('cardNumber', '^\\d{16}$'),
rules.isNotNull('cvv'),
rules.regex('cvv', '^\\d{3,4}$')
);
// Complete form validation
const completeFormValidation = rules.and(
step1Validation,
step2Validation,
step3Validation
);
// Validate incrementally
function validateStep(stepNumber, data) {
const validations = [
step1Validation,
step2Validation,
step3Validation
];
return engine.evaluateExpr(validations[stepNumber - 1], data);
}
const formData = {
firstName: 'John',
lastName: 'Doe',
email: 'john@example.com',
street: '123 Main St',
city: 'NYC',
state: 'NY',
zip: '10001',
cardNumber: '1234567890123456',
cvv: '123'
};
// Validate step 1
validateStep(1, formData); // { success: true }
// Validate step 2
validateStep(2, formData); // { success: true }
// Validate complete form
engine.evaluateExpr(completeFormValidation, formData);
// { success: true }
Dynamic Validation
Validation based on user type.Copy
function createUserValidation(userType) {
const baseValidation = rules.and(
rules.isNotNull('email'),
rules.isNotNull('password'),
rules.gte('password.length', 8)
);
if (userType === 'business') {
return rules.and(
baseValidation,
rules.isNotNull('companyName'),
rules.isNotNull('taxId'),
rules.regex('taxId', '^\\d{9}$')
);
}
if (userType === 'individual') {
return rules.and(
baseValidation,
rules.isNotNull('firstName'),
rules.isNotNull('lastName'),
rules.gte('age', 18)
);
}
return baseValidation;
}
// Business user
const businessUser = {
email: 'contact@company.com',
password: 'securepass123',
companyName: 'Acme Corp',
taxId: '123456789'
};
const businessRule = createUserValidation('business');
engine.evaluateExpr(businessRule, businessUser);
// { success: true }
// Individual user
const individualUser = {
email: 'john@example.com',
password: 'securepass123',
firstName: 'John',
lastName: 'Doe',
age: 30
};
const individualRule = createUserValidation('individual');
engine.evaluateExpr(individualRule, individualUser);
// { success: true }
Comprehensive E-commerce Validation
Copy
const checkoutValidation = {
and: [
// Cart validation
{ gt: ['cart.items.length', 0] },
{ lte: ['cart.items.length', 100] },
{ gte: ['cart.total', 0.01] },
// User validation
{ eq: ['user.verified', true] },
{ eq: ['user.status', 'active'] },
{ notIn: ['user.id', 'blockedUsers'] },
// Shipping validation
{
or: [
{ eq: ['shipping.method', 'pickup'] },
{
and: [
{ in: ['shipping.method', ['standard', 'express', 'overnight']] },
{ isNotNull: ['shipping.address.street'] },
{ isNotNull: ['shipping.address.city'] },
{ isNotNull: ['shipping.address.zip'] }
]
}
]
},
// Payment validation
{ isNotNull: ['payment.method'] },
{
or: [
{ eq: ['payment.method', 'card'] },
{ eq: ['payment.method', 'paypal'] },
{ eq: ['payment.method', 'crypto'] }
]
},
// Inventory check
{ eq: ['inventory.available', true] },
// Coupon validation (if present)
{
or: [
{ isNull: ['coupon'] },
{
and: [
{ isNotNull: ['coupon.code'] },
{ eq: ['coupon.valid', true] },
{ gte: ['coupon.expiresAt', 'currentDate'] }
]
}
]
}
]
};
const checkoutData = {
cart: {
items: [{ id: 1, price: 29.99 }],
total: 29.99
},
user: {
id: 123,
verified: true,
status: 'active'
},
shipping: {
method: 'standard',
address: {
street: '123 Main St',
city: 'NYC',
zip: '10001'
}
},
payment: {
method: 'card'
},
inventory: {
available: true
},
coupon: null,
blockedUsers: [],
currentDate: '2024-12-31'
};
engine.evaluateExpr(checkoutValidation, checkoutData);
// { success: true }
Best Practices
1. Validate in Layers
1. Validate in Layers
Basic → Format → Business rules
2. Use Conditional Validation
2. Use Conditional Validation
Validate only what’s needed based on context
3. Reuse Validation Rules
3. Reuse Validation Rules
Extract common validations as constants
4. Fail Fast
4. Fail Fast
Put simple checks first to exit early
5. Clear Error Messages
5. Clear Error Messages
Structure rules to identify which part failed
6. Test Edge Cases
6. Test Edge Cases
Test null, undefined, empty values
