✅ The Validator Service
The Validator service provides a fluent, chainable API for validating user input. It's automatically available in your controllers through Odac.Validator.
Basic Usage
The validator uses a method-chaining pattern:
const validator = Odac.Validator
validator.post('email').check('required|email').message('Valid email required')
validator.post('password').check('required|minlen:8').message('Password must be at least 8 characters')
if (await validator.error()) {
return validator.result('Validation failed')
}
Available Methods
post(key)- Validate a POST fieldget(key)- Validate a GET fieldvar(name, value)- Validate a custom variablefile(name)- Validate a file uploadcheck(rules)- Define validation rules (pipe-separated)message(text)- Set custom error messageerror()- Returns true if validation failed (async)result(message, data)- Returns formatted result object (async)success(callback)- Returns success result with databrute(maxAttempts)- Enable brute force protection (default: 5 attempts)
Validation Rules
Type Validation:
required- Field cannot be emptyaccepted- Must be 1, 'on', 'yes', or truenumeric- Must be a numberalpha- Only alphabetic charactersalphaspace- Alphabetic characters and spacesalphanumeric- Alphanumeric characters onlyalphanumericspace- Alphanumeric characters and spacesusername- Alphanumeric username (no spaces or special chars)email- Valid email addressip- Valid IP addressfloat- Floating point numbermac- Valid MAC addressdomain- Valid domain nameurl- Valid URLarray- Must be an arraydate- Valid date format
Length Validation:
len:X- Exact length must be Xminlen:X- Minimum length of X charactersmaxlen:X- Maximum length of X characters
Value Validation:
min:X- Minimum value of Xmax:X- Maximum value of Xequal:value- Must equal specific valuenot:value- Must not equal specific valuesame:field- Must match another fielddifferent:field- Must differ from another field
Date Validation:
mindate:date- Must be after specified datemaxdate:date- Must be before specified date
String Validation:
in:substring- Must contain substringnotin:substring- Must not contain substringregex:pattern- Must match regex pattern!disposable- Block disposable/temporary email providers (List is automatically updated daily)
Security:
xss- Check for HTML tags (XSS protection)usercheck- User must be authenticateduser:field- Must match authenticated user's field value
Inverse Rules:
Use ! prefix to invert any rule: !required, !email, etc.
Example: User Registration
module.exports = async function (Odac) {
const validator = Odac.Validator
validator.post('username').check('required|username|minlen:4|maxlen:20').message('Username must be 4-20 alphanumeric characters')
validator.post('email').check('required|email').message('Valid email address required')
validator.post('password').check('required|minlen:8').message('Password must be at least 8 characters')
validator.post('password_confirm').check('required|same:password').message('Passwords must match')
if (await validator.error()) {
return validator.result('Validation failed')
}
return validator.success('User registered successfully')
}
Example: Login with Brute Force Protection
module.exports = async function (Odac) {
const validator = Odac.Validator
validator.post('email').check('required|email').message('Email required')
validator.post('password').check('required').message('Password required')
validator.brute(5)
if (await validator.error()) {
return validator.result('Login failed')
}
return validator.success({token: 'abc123'})
}
Example: Custom Variable Validation
module.exports = async function (Odac) {
const validator = Odac.Validator
const customValue = calculateSomething()
validator.var('calculated_value', customValue).check('numeric|min:100|max:1000').message('Value must be between 100 and 1000')
if (await validator.error()) {
return validator.result('Invalid calculation')
}
return validator.success('Calculation valid')
}
Multiple Checks Per Field
You can chain multiple check() calls for the same field, each with its own specific error message. The validator will return the first error it encounters:
module.exports = async function (Odac) {
const validator = Odac.Validator
validator
.post('password')
.check('required').message('Password is required')
.check('minlen:8').message('Password must be at least 8 characters')
.check('maxlen:50').message('Password cannot exceed 50 characters')
.check('regex:[A-Z]').message('Password must contain at least one uppercase letter')
.check('regex:[0-9]').message('Password must contain at least one number')
if (await validator.error()) {
return validator.result('Validation failed')
}
return validator.success('Password is strong')
}
Example: Complex Form Validation
module.exports = async function (Odac) {
const validator = Odac.Validator
validator
.post('username')
.check('required').message('Username is required')
.check('username').message('Username can only contain letters and numbers')
.check('minlen:3').message('Username must be at least 3 characters')
.check('maxlen:20').message('Username cannot exceed 20 characters')
validator
.post('email')
.check('required').message('Email is required')
.check('email').message('Please enter a valid email address')
validator
.post('age')
.check('required').message('Age is required')
.check('numeric').message('Age must be a number')
.check('min:18').message('You must be at least 18 years old')
.check('max:120').message('Please enter a valid age')
validator
.post('website')
.check('!required').message('Website is optional')
.check('url').message('Please enter a valid URL')
validator
.post('bio')
.check('maxlen:500').message('Bio cannot exceed 500 characters')
.check('xss').message('Bio contains invalid HTML tags')
if (await validator.error()) {
return validator.result('Please fix the errors')
}
return validator.success('Profile updated successfully')
}
Example: Date Range Validation
module.exports = async function (Odac) {
const validator = Odac.Validator
validator
.post('start_date')
.check('required').message('Start date is required')
.check('date').message('Invalid date format')
.check('mindate:2024-01-01').message('Start date must be after January 1, 2024')
validator
.post('end_date')
.check('required').message('End date is required')
.check('date').message('Invalid date format')
.check('maxdate:2025-12-31').message('End date must be before December 31, 2025')
if (await validator.error()) {
return validator.result('Invalid date range')
}
return validator.success('Date range is valid')
}
Example: Conditional Validation with Custom Variables
module.exports = async function (Odac) {
const validator = Odac.Validator
const userRole = Odac.Auth.user('role')
const userCredits = Odac.Auth.user('credits') || 0
validator.post('title').check('required').message('Title is required')
validator.post('content').check('required').message('Content is required')
if (userRole !== 'premium') {
validator
.var('user_credits', userCredits)
.check('numeric').message('Invalid credits value')
.check('min:10').message('You need at least 10 credits to publish')
validator
.post('content')
.check('maxlen:1000').message('Free users are limited to 1000 characters')
}
validator
.var('role_check', userRole)
.check('!equal:banned').message('Your account has been banned')
if (await validator.error()) {
return validator.result('Validation failed')
}
return validator.success('Article published')
}
Example: User Authentication Validation
module.exports = async function (Odac) {
const validator = Odac.Validator
validator
.post('current_password')
.check('required').message('Current password is required')
.check('user:password').message('Current password is incorrect')
validator
.post('new_password')
.check('required').message('New password is required')
.check('minlen:8').message('New password must be at least 8 characters')
.check('different:current_password').message('New password must be different from current')
if (await validator.error()) {
return validator.result('Password change failed')
}
return validator.success('Password changed successfully')
}
Example: Boolean Function Results
You can use boolean values directly in check() for custom validation logic:
module.exports = async function (Odac) {
const validator = Odac.Validator
const userId = await Odac.request('user_id')
const isOwner = await checkIfUserOwnsResource(userId)
const hasPermission = await checkUserPermission('edit')
const isWithinLimit = await checkDailyLimit(userId)
validator.post('title').check('required').message('Title is required')
validator
.var('ownership', null)
.check(isOwner).message('You do not own this resource')
validator
.var('permission', null)
.check(hasPermission).message('You do not have permission to edit')
validator
.var('limit', null)
.check(isWithinLimit).message('You have reached your daily edit limit')
if (await validator.error()) {
return validator.result('Validation failed')
}
return validator.success('Resource updated')
}
Example: Chained Validation (Single Statement)
module.exports = async function (Odac) {
return await Odac.Validator
.post('email').check('required').message('Email required').check('email').message('Invalid email')
.post('password').check('required').message('Password required').check('minlen:8').message('Min 8 chars')
.post('age').check('required').message('Age required').check('numeric').message('Must be number').check('min:18').message('Must be 18+')
.success('Registration successful')
}
Example: Admin-Only Action with User Check
module.exports = async function (Odac) {
const validator = Odac.Validator
validator
.var('auth_check', null)
.check('usercheck').message('You must be logged in')
validator
.var('admin_role', null)
.check('user:role').message('Admin access required')
.check('equal:admin').message('Only admins can perform this action')
validator.post('action').check('required').message('Action is required')
if (await validator.error()) {
return validator.result('Access denied')
}
return validator.success('Action completed')
}
Frontend Integration
When using Odac.form() on the frontend, validation errors are automatically displayed:
Automatic Error Display:
- Each field's error message appears below the input with attribute
[odac-form-error="fieldname"] - Invalid inputs get the
_odac_errorCSS class automatically - Errors fade out when the user focuses on the input
Success Messages:
- Success messages appear in elements with
[odac-form-success]attribute - Automatically fades in when validation passes
Auto-Redirect:
- If you pass a URL as the second parameter to
Odac.form(), successful submissions automatically redirect:Odac.form('myForm', '/dashboard') // Redirects to /dashboard on success
Example HTML:
<form odac-form="register" action="/api/register" method="POST">
<input type="email" name="email" placeholder="Email">
<span odac-form-error="email"></span>
<input type="password" name="password" placeholder="Password">
<span odac-form-error="password"></span>
<button type="submit">Register</button>
<span odac-form-success></span>
</form>
<script>
Odac.form('register', '/dashboard') // Auto-redirect on success
</script>
Response Format
The result() method returns a standardized response:
Success:
{
"result": {
"success": true,
"message": "Operation successful"
},
"data": null
}
Error:
{
"result": {
"success": false
},
"errors": {
"email": "Valid email address required",
"password": "Password must be at least 8 characters"
}
}