Fast, type-safe validation library with async, conditional, nested schema, pipeline, batch, and benchmark support. Zero dependencies. Works in Node.js and browser.
npm install @incogdev/validateimport { validate } from '@incogdev/validate';
const result = validate('user@example.com')
.required()
.email()
.isValid();
console.log(result); // true- Basic Rules
- Chainable Validator
- Custom Error Messages
- Schema Validation
- Custom Rules
- Async Validation
- Conditional Validation
- Nested Schema
- Pipeline / Transform
- Batch Validation
- Benchmarking
- TypeScript Support
- Framework Examples
Import individual rules for simple validation:
import { rules } from '@incogdev/validate';
// Email validation
rules.email('john@example.com'); // true
rules.email('invalid-email'); // false
// Phone number (international format)
rules.phone('+1234567890'); // true
rules.phone('+1 (555) 123-4567'); // true
rules.phone('abc123'); // false
// URL validation
rules.url('https://google.com'); // true
rules.url('http://localhost:3000'); // true
rules.url('not-a-url'); // false
// Check if value is not empty
rules.notEmpty('hello'); // true
rules.notEmpty(''); // false
rules.notEmpty(null); // false
rules.notEmpty(undefined); // false
// Number validation
rules.isNumber(42); // true
rules.isNumber('3.14'); // true
rules.isNumber('abc'); // false
// Integer validation
rules.isInt(42); // true
rules.isInt(3.14); // false
rules.isInt('100'); // true
// Boolean validation
rules.isBoolean(true); // true
rules.isBoolean(false); // true
rules.isBoolean('true'); // true
rules.isBoolean('false'); // true
rules.isBoolean('yes'); // false
// String length
rules.minLength('hello', 3); // true
rules.minLength('hi', 3); // false
rules.maxLength('hello', 10); // true
rules.maxLength('hello world', 5); // false
// Number range
rules.between(50, 0, 100); // true
rules.between(150, 0, 100); // false
// Regex pattern matching
rules.matches('abc123', /^[a-z]{3}\d{3}$/); // true
rules.matches('abc', /^[a-z]{3}\d{3}$/); // false
// Value must be in allowed list
rules.oneOf('apple', ['apple', 'banana', 'orange']); // true
rules.oneOf('grape', ['apple', 'banana', 'orange']); // false
// Strong password (min 8 chars, uppercase, lowercase, number, special char)
rules.strongPassword('Pass123!'); // true
rules.strongPassword('weak'); // false
rules.strongPassword('password123'); // false
// Postal code by country
rules.postalCode('90210'); // true (US default)
rules.postalCode('90210-1234'); // true (US zip+4)
rules.postalCode('M5V 2T6', 'CA'); // true (Canada)
rules.postalCode('SW1A 1AA', 'UK'); // true (UK)
// Credit card (Luhn algorithm)
rules.creditCard('4532015112830366'); // true (Visa test number)
rules.creditCard('1234567890123456'); // false
// IP address
rules.ip('192.168.1.1'); // true (IPv4 default)
rules.ip('::1'); // true (IPv6)
rules.ip('999.999.999.999'); // falseThe chainable validator allows multiple validations on the same value:
import { validate } from '@incogdev/validate';
const validator = validate('john@example.com')
.required()
.email()
.minLength(5)
.maxLength(100);
console.log(validator.isValid()); // true
console.log(validator.getErrors()); // []const validator = validate('invalid')
.required()
.email();
if (validator.isValid()) {
console.log('Email is valid!');
} else {
console.log('Errors:', validator.getErrors());
}
// Output: Errors: ['Invalid email address']| Method | Description |
|---|---|
.email() |
Validates email format |
.phone() |
Validates phone number |
.url() |
Validates URL |
.required() |
Value cannot be empty |
.minLength(n) |
Minimum character length |
.maxLength(n) |
Maximum character length |
.isNumber() |
Must be a number |
.strongPassword() |
Password strength check |
.between(min, max) |
Number between range |
.matches(regex) |
Matches regex pattern |
.oneOf([...]) |
Value must be in array |
.custom(fn, msg) |
Custom validation function |
Methods can be chained in any order. All validations run regardless of previous failures:
const result = validate('')
.required() // fails
.email() // fails
.minLength(3) // fails
.isValid(); // false
console.log(validator.getErrors());
// ['Field is required', 'Invalid email address', 'Must be at least 3 characters']After pipeline transforms, you can get the final value:
const validator = validate(' hello@world.com ')
.pipe(v => v.trim())
.pipe(v => v.toLowerCase())
.email();
console.log(validator.getValue()); // 'hello@world.com'Override default error messages:
import { validate } from '@incogdev/validate';
const validator = validate('', {
required: 'Username cannot be empty!',
email: 'Please enter a valid email address'
})
.required()
.email();
console.log(validator.getErrors());
// ['Username cannot be empty!', 'Please enter a valid email address']| Key | Default Message |
|---|---|
email |
"Invalid email address" |
phone |
"Invalid phone number" |
url |
"Invalid URL" |
required |
"Field is required" |
minLength |
"Must be at least X characters" |
maxLength |
"Must be at most X characters" |
isNumber |
"Must be a number" |
strongPassword |
"Password must contain uppercase, lowercase, number, and symbol" |
between |
"Must be between X and Y" |
matches |
"Value does not match required pattern" |
oneOf |
"Value must be one of: ..." |
Validate entire objects with a schema definition.
import { schema } from '@incogdev/validate';
const userSchema = schema({
name: { type: 'string', required: true },
email: { type: 'email', required: true },
age: { type: 'number' }
});
const result = userSchema.validate({
name: 'John Doe',
email: 'john@example.com',
age: 25
});
console.log(result.valid); // true
console.log(result.errors); // {}const userSchema = schema({
username: {
type: 'string',
required: true,
min: 3,
max: 20
},
bio: {
type: 'string',
max: 200
}
});
const result = userSchema.validate({
username: 'jo', // too short
bio: 'a'.repeat(201) // too long
});
console.log(result.valid); // false
console.log(result.errors);
// {
// username: ['username must be at least 3 characters'],
// bio: ['bio must be at most 200 characters']
// }const productSchema = schema({
sku: {
type: 'string',
required: true,
pattern: /^[A-Z]{3}-\d{4}$/,
patternMessage: 'SKU must be format XXX-0000'
}
});
const result = productSchema.validate({ sku: 'ABC-123' });
console.log(result.valid); // false
console.log(result.errors.sku[0]);const orderSchema = schema({
total: {
type: 'number',
custom: (value) => value > 0,
customMessage: 'Total must be greater than 0'
}
});
const result = orderSchema.validate({ total: -10 });
console.log(result.valid); // false
console.log(result.errors.total[0]); // "Total must be greater than 0"| Type | Description | Validation |
|---|---|---|
'string' |
Any string | typeof value === 'string' |
'number' |
Any number | typeof value === 'number' |
'boolean' |
Boolean | typeof value === 'boolean' |
'email' |
Email format | Uses rules.email() |
'phone' |
Phone number | Uses rules.phone() |
'url' |
URL format | Uses rules.url() |
| Option | Type | Description |
|---|---|---|
type |
string | One of the types above |
required |
boolean | If true, value cannot be empty |
min |
number | Minimum length (strings) or value (numbers) |
max |
number | Maximum length (strings) or value (numbers) |
pattern |
RegExp | Regex pattern to match |
patternMessage |
string | Custom message for pattern failure |
custom |
function | Custom validation function |
customMessage |
string | Custom message for custom validation |
message |
string | Custom message for required failure |
Create your own validation rules using the .custom() method.
import { validate } from '@incogdev/validate';
const validator = validate(7)
.custom((value) => value % 2 === 0, 'Number must be even');
console.log(validator.isValid()); // false
console.log(validator.getErrors()); // ['Number must be even']function isDivisibleBy(divisor) {
return (value) => value % divisor === 0;
}
const validator = validate(10)
.custom(isDivisibleBy(3), 'Must be divisible by 3');
console.log(validator.isValid()); // falseconst validator = validate('abc123')
.required()
.minLength(3)
.custom((value) => /^\w+$/.test(value), 'Only letters, numbers, and underscores')
.isValid();Validate values asynchronously (e.g., checking if email exists in database).
import { validateAsync } from '@incogdev/validate';
async function checkEmail(email) {
const { valid, errors } = await validateAsync(email, (v) =>
v.required().email()
);
if (!valid) {
console.log('Invalid email:', errors);
}
return valid;
}import { validate } from '@incogdev/validate';
async function validateUser(email) {
const validator = validate(email)
.required()
.email()
.customAsync(async (value) => {
// Simulate database check
const exists = await db.users.findOne({ email: value });
return !exists;
}, 'Email already taken');
const isValid = await validator.isValidAsync();
return {
valid: isValid,
errors: validator.getErrors()
};
}import { createAsyncValidator } from '@incogdev/validate';
const validator = createAsyncValidator('test@example.com')
.addRule(v => v.required().email())
.addAsyncRule(async (value) => {
const exists = await checkDatabase(value);
return !exists;
}, 'Email already exists');
const result = await validator.validate();
console.log(result.valid, result.errors);Apply validation rules only when certain conditions are met.
import { conditional } from '@incogdev/validate';
const password = 'weak';
const result = conditional(password, [
{
condition: (v) => v.length < 8,
rules: (v) => v.custom(() => false, 'Password must be at least 8 characters')
},
{
condition: (v) => /^[a-z]+$/.test(v),
rules: (v) => v.custom(() => false, 'Password must contain uppercase or numbers')
}
]);
console.log(result.valid); // false
console.log(result.errors); // ['Password must be at least 8 characters', 'Password must contain uppercase or numbers']import { validate } from '@incogdev/validate';
const isAdmin = true;
const password = 'admin123';
const validator = validate(password)
.required()
.minLength(5)
.if(() => isAdmin, (v) => v.strongPassword())
.unless(() => isAdmin, (v) => v.minLength(3));
console.log(validator.isValid()); // false if admin and password not strongimport { createConditionalValidator } from '@incogdev/validate';
const result = createConditionalValidator('user@example.com')
.if(v => v.includes('admin'), v => v.email())
.if(v => v.length > 10, v => v.minLength(5))
.validate();
console.log(result.valid, result.errors);Validate deeply nested objects.
import { nestedSchema } from '@incogdev/validate';
const userSchema = nestedSchema({
name: { type: 'string', required: true, min: 2 },
address: {
street: { type: 'string', required: true },
city: { type: 'string', required: true },
zipCode: { type: 'string', pattern: /^\d{5}$/, patternMessage: 'Invalid zip code' }
}
});
const result = userSchema.validate({
name: 'John',
address: {
street: '123 Main St',
city: '',
zipCode: '1234'
}
});
console.log(result.valid); // false
console.log(result.errors);
// {
// 'address.city': ['city is required'],
// 'address.zipCode': ['Invalid zip code']
// }import { deepValidate } from '@incogdev/validate';
const schemaDef = {
email: { type: 'email', required: true }
};
const result = deepValidate({ email: 'invalid' }, schemaDef, 'user');
console.log(result.errors);
// { 'user.email': ['user.email must be a email'] }const userSchema = nestedSchema({
name: { type: 'string', required: true },
email: { type: 'email', required: true }
});
// Validate only provided fields
const partialResult = userSchema.partial({ email: 'test@example.com' });
console.log(partialResult.valid); // true (name not required in partial mode)Transform values before validation.
import { createPipeline } from '@incogdev/validate';
const result = await createPipeline(' HELLO@WORLD.com ')
.pipe(v => v.trim()) // Remove whitespace
.pipe(v => v.toLowerCase()) // Convert to lowercase
.validate(v => v.email()) // Validate email
.execute();
console.log(result.valid); // true
console.log(result.value); // 'hello@world.com'const pipeline = createPipeline(' User@Example.com ')
.pipe(v => v.trim())
.pipe(v => v.toLowerCase())
.validate(v => v.required().email())
.validate(v => v.custom(v => v.includes('user'), 'Must contain "user"'));
const result = await pipeline.execute();
console.log(result.valid); // true
console.log(result.value); // 'user@example.com'Validate multiple fields at once.
import { batchValidate, batchResultSummary } from '@incogdev/validate';
const results = batchValidate([
{ field: 'email', value: 'test@example.com', rules: (v) => v.required().email() },
{ field: 'password', value: 'weak', rules: (v) => v.required().strongPassword() },
{ field: 'age', value: 25, rules: (v) => v.required().isNumber().between(0, 120) }
]);
const summary = batchResultSummary(results);
console.log(summary.allValid); // false
console.log(summary.validCount); // 2 (email and age are valid)
console.log(summary.invalidCount); // 1 (password invalid)
console.log(summary.errors);
// { password: ['Password must contain uppercase, lowercase, number, and symbol'] }import { batchValidateAsync } from '@incogdev/validate';
const results = await batchValidateAsync([
{ field: 'email', value: 'test@example.com', rules: (v) => v.email() },
{ field: 'unique', value: 'taken', rules: (v) => v.customAsync(async (val) => {
return await checkUniqueness(val);
}, 'Value already taken') }
]);
console.log(results);const results = batchValidate([
{ field: 'email', value: 'invalid', rules: (v) => v.email() }
]);
for (const result of results) {
console.log(`${result.field}: ${result.valid ? '✓' : '✗'}`);
if (!result.valid) {
console.log(` Errors: ${result.errors.join(', ')}`);
}
}Measure performance of your validations.
import { benchmark, compareBenchmarks } from '@incogdev/validate';
const result = benchmark(() => {
validate('test@example.com')
.required()
.email()
.minLength(5)
.isValid();
}, 10000, 'email-validation');
console.log(`${result.opsPerSecond} ops/sec`);
console.log(`${result.averageTimeMs.toFixed(4)}ms average`);const results = [
benchmark(() => validate('test@example.com').email().isValid(), 10000, 'email-only'),
benchmark(() => validate('test@example.com').required().email().minLength(5).maxLength(100).isValid(), 10000, 'full-validation'),
benchmark(() => rules.email('test@example.com'), 10000, 'rules-direct')
];
compareBenchmarks(results);import { createBenchmarkSuite } from '@incogdev/validate';
const suite = createBenchmarkSuite()
.add('email validation', () => {
rules.email('test@example.com');
}, 10000)
.add('chainable validation', () => {
validate('test@example.com').email().isValid();
}, 10000)
.add('full validation', () => {
validate('test@example.com')
.required()
.email()
.minLength(5)
.maxLength(100)
.isValid();
}, 10000);
const results = suite.runAndCompare();import { benchmarkAsync } from '@incogdev/validate';
const result = await benchmarkAsync(async () => {
await validateAsync('test@example.com', v => v.required().email());
}, 1000, 'async-validation');
console.log(`${result.opsPerSecond} async ops/sec`);| Property | Type | Description |
|---|---|---|
name |
string | Benchmark name |
operations |
number | Number of operations run |
totalTimeMs |
number | Total time in milliseconds |
averageTimeMs |
number | Average time per operation |
opsPerSecond |
number | Operations per second |
Full TypeScript support with type inference.
import { validate, rules, schema, Validator } from '@incogdev/validate';
// Rules return boolean
const isValid: boolean = rules.email('test@example.com');
// Validator returns typed instance
const validator: Validator<string> = validate('test')
.required()
.minLength(3);
// Get typed value
const value: string = validator.getValue();interface User {
name: string;
email: string;
age?: number;
}
const userSchema = schema<User>({
name: { type: 'string', required: true, min: 2 },
email: { type: 'email', required: true },
age: { type: 'number' }
});
const user: User = { name: 'John', email: 'john@example.com' };
const result = userSchema.validate(user);function validateUserData<T extends { email: string }>(data: T) {
const emailValid = validate(data.email).required().email().isValid();
return { ...data, emailValid };
}interface ValidationResult<T> {
valid: boolean;
errors: string[];
value: T;
}
async function validateEmail(value: string): Promise<ValidationResult<string>> {
return await validateAsync(value, v => v.required().email());
}import { validate } from '@incogdev/validate';
app.post('/register', (req, res) => {
const { email, password, username } = req.body;
const emailValid = validate(email).required().email();
const passwordValid = validate(password).required().strongPassword();
const usernameValid = validate(username).required().minLength(3).maxLength(20);
if (!emailValid.isValid() || !passwordValid.isValid() || !usernameValid.isValid()) {
return res.status(400).json({
errors: {
email: emailValid.getErrors(),
password: passwordValid.getErrors(),
username: usernameValid.getErrors()
}
});
}
// Proceed with registration
res.json({ message: 'User registered' });
});app.post('/product', (req, res) => {
const productSchema = schema({
name: { type: 'string', required: true, min: 1, max: 100 },
price: { type: 'number', required: true, min: 0 },
category: { type: 'string', required: true }
});
const result = productSchema.validate(req.body);
if (!result.valid) {
return res.status(400).json({ errors: result.errors });
}
// Proceed
});import { useState } from 'react';
import { validate } from '@incogdev/validate';
function RegistrationForm() {
const [formData, setFormData] = useState({ email: '', password: '' });
const [errors, setErrors] = useState({});
const handleSubmit = (e) => {
e.preventDefault();
const emailValid = validate(formData.email).required().email();
const passwordValid = validate(formData.password).required().strongPassword();
const newErrors = {};
if (!emailValid.isValid()) newErrors.email = emailValid.getErrors()[0];
if (!passwordValid.isValid()) newErrors.password = passwordValid.getErrors()[0];
if (Object.keys(newErrors).length > 0) {
setErrors(newErrors);
return;
}
// Submit form
};
return (
<form onSubmit={handleSubmit}>
<input
value={formData.email}
onChange={(e) => setFormData({ ...formData, email: e.target.value })}
/>
{errors.email && <span className="error">{errors.email}</span>}
<input
type="password"
value={formData.password}
onChange={(e) => setFormData({ ...formData, password: e.target.value })}
/>
{errors.password && <span className="error">{errors.password}</span>}
<button type="submit">Register</button>
</form>
);
}import { NextResponse } from 'next/server';
import { validate, schema } from '@incogdev/validate';
export async function POST(request: Request) {
const body = await request.json();
const userSchema = schema({
name: { type: 'string', required: true, min: 2 },
email: { type: 'email', required: true },
password: { type: 'string', required: true, min: 8 }
});
const result = userSchema.validate(body);
if (!result.valid) {
return NextResponse.json({ errors: result.errors }, { status: 400 });
}
// Create user
return NextResponse.json({ success: true });
}import fastify from 'fastify';
import { validate } from '@incogdev/validate';
const app = fastify();
app.post('/login', async (req, reply) => {
const { email, password } = req.body;
const validator = validate(email).required().email();
if (!validator.isValid()) {
return reply.status(400).send({ error: validator.getErrors() });
}
return { success: true };
});const { rules, validate, schema } = require('@incogdev/validate');import { rules, validate, schema } from '@incogdev/validate';
// Import specific features
import { validateAsync } from '@incogdev/validate/async';
import { conditional } from '@incogdev/validate/conditional';
import { nestedSchema } from '@incogdev/validate/nested';
import { createPipeline } from '@incogdev/validate/pipeline';
import { batchValidate } from '@incogdev/validate/batch';
import { benchmark } from '@incogdev/validate/benchmark';<script type="importmap">
{
"imports": {
"@incogdev/validate": "https://unpkg.com/@incogdev/validate@4.0.0/dist/browser.js"
}
}
</script>
<script type="module">
import { validate } from '@incogdev/validate';
const result = validate('test@example.com').email().isValid();
console.log(result);
</script>