
You can use the library to validate single values with chainable rules, model full objects with schemas, add custom business rules, run async checks, and return structured errors.
Features:
- Validate emails, URLs, phone numbers, passwords, cards, IP addresses, and numeric ranges.
- Chain multiple checks on one value.
- Model full objects with schema definitions.
- Add custom synchronous and asynchronous business rules.
- Transform values before validation steps.
- Check nested objects and partial payloads.
- Run batch validation across multiple fields.
- Measure validator speed with benchmark helpers.
- Use typed validators in TypeScript projects.
Use Cases:
- Registration and login forms require email format, password strength, and username length checks.
- E-commerce checkout forms that validate shipping address fields, postal codes by country, and credit card numbers.
- Node.js and Express API routes that validate request body objects against a schema before processing.
- Next.js or React applications that need per-field inline error feedback before form submission.
Getting Started:
Installation
Install the package from npm:
npm install @incogdev/validate
The library also provides a browser build through the unpkg CDN:
<script type="importmap">
{
"imports": {
"@incogdev/validate": "https://unpkg.com/@incogdev/validate/dist/browser.js"
}
}
</script>
<script type="module">
import { validate } from '@incogdev/validate';
const result = validate('contact@mysite.com').email().isValid();
console.log(result); // true
</script>
Use CommonJS require syntax in Node.js without a bundler:
const { rules, validate, schema } = require('@incogdev/validate');
Basic Usage
The validate() function accepts a value and returns a chainable validator instance. Chain the rule methods you need, then call isValid() for a boolean result or getErrors() for the full error array.
import { validate } from '@incogdev/validate';
// Chain multiple rules on a single email field
const emailCheck = validate('contact@cssscript.com')
.required() // value cannot be empty
.email() // must match email format
.maxLength(100); // no more than 100 characters
console.log(emailCheck.isValid()); // true
console.log(emailCheck.getErrors()); // []
// Example with a failing value
const failedCheck = validate('not-an-email')
.required()
.email();
console.log(failedCheck.getErrors()); // ['Invalid email address']
For quick single-rule boolean checks without chaining, import rules directly:
import { rules } from '@incogdev/validate';
rules.email('admin@store.com'); // true
rules.strongPassword('Pass123!'); // true
rules.postalCode('M5V 2T6', 'CA'); // true (Canadian format)
rules.creditCard('4532015112830366'); // true (Luhn algorithm check)
rules.ip('10.0.0.1', 4); // true (IPv4)
More Usages
Schema Validation for Object Bodies
API routes and form handlers often receive a full object rather than individual fields. The schema() function takes a field definition map and returns a validator whose .validate() method checks any object against it.
import { schema } from '@incogdev/validate';
const registrationSchema = schema({
username: { type: 'string', required: true, min: 3, max: 20 },
email: { type: 'email', required: true },
age: { type: 'number', min: 18, max: 120 }
});
const result = registrationSchema.validate({
username: 'jo', // fails: too short
email: 'admin@store.com', // passes
age: 14 // fails: below minimum
});
console.log(result.valid); // false
console.log(result.errors);
// {
// username: ['username must be at least 3 characters'],
// age: ['age must be at least 18 characters']
// }
Async Validation for Uniqueness Checks
Registration forms often need to confirm that an email or username does not already exist in the database. The validateAsync function accepts the value and a callback that builds the rule chain, then resolves a { valid, errors, value } object.
import { validateAsync } from '@incogdev/validate';
async function checkRegistrationEmail(email) {
const { valid, errors } = await validateAsync(email, (v) =>
v.required().email().customAsync(async (val) => {
// Replace with your actual database query
const taken = await db.users.exists({ email: val });
return !taken; // return false if email is already registered
}, 'That email address is already registered')
);
return { valid, errors };
}
Conditional Rules for Role-Based Requirements
Password policies often differ by user role. The .if() method on the chainable validator applies extra rules only when a predicate returns true.
import { validate } from '@incogdev/validate';
function validatePassword(password, isAdminAccount) {
const result = validate(password)
.required()
.minLength(8)
.if(() => isAdminAccount, (v) => v.strongPassword());
// strong password only required for admin accounts
return {
valid: result.isValid(),
errors: result.getErrors()
};
}
Nested Schema Validation for Address Objects
Shipping forms typically contain address sub-objects. The nestedSchema() function validates nested fields and returns errors keyed by dot-notated paths such as address.zipCode.
import { nestedSchema } from '@incogdev/validate';
const orderSchema = nestedSchema({
customerName: { type: 'string', required: true, min: 2 },
address: {
street: { type: 'string', required: true },
city: { type: 'string', required: true },
zip: {
type: 'string',
pattern: /^d{5}$/,
patternMessage: 'ZIP code must be 5 digits'
}
}
});
const result = orderSchema.validate({
customerName: 'Sarah',
address: {
street: '742 Elm Street',
city: '', // fails: required
zip: '900' // fails: wrong format
}
});
console.log(result.errors);
// {
// 'address.city': ['city is required'],
// 'address.zip': ['ZIP code must be 5 digits']
// }
Batch Validation for Full Form Submission
Batch processing validates every form field in one call and retrieves a summary. This pattern collects all errors at form submission time rather than stopping at the first failure.
import { batchValidate, batchResultSummary } from '@incogdev/validate';
const results = batchValidate([
{ field: 'email', value: 'customer@shop.com', rules: (v) => v.required().email() },
{ field: 'phone', value: '+12025551234', rules: (v) => v.required().phone() },
{ field: 'password', value: 'abc', rules: (v) => v.required().strongPassword() }
]);
const summary = batchResultSummary(results);
console.log(summary.allValid); // false
console.log(summary.invalidCount); // 1
console.log(summary.errors);
// { password: ['Password must contain uppercase, lowercase, number, and symbol'] }
Pipeline: Transform Then Validate
The createPipeline() function lets you clean up a value before any rule runs. It suits cases where user input may arrive with extra whitespace or inconsistent casing.
import { createPipeline } from '@incogdev/validate';
const result = await createPipeline(' NEWSLETTER@BRAND.COM ')
.pipe(v => v.trim()) // remove surrounding whitespace
.pipe(v => v.toLowerCase()) // normalize to lowercase
.validate(v => v.required().email())
.execute();
console.log(result.valid); // true
console.log(result.value); // 'newsletter@brand.com'
Custom Error Messages
Pass a message map as the second argument to validate() to override any default error text.
import { validate } from '@incogdev/validate';
const usernameCheck = validate('', {
required: 'Please enter a username.',
minLength: 'Username must be at least 3 characters long.'
})
.required()
.minLength(3);
console.log(usernameCheck.getErrors());
// ['Please enter a username.']
Configuration Options:
Schema field definitions accept these options:
type(string): The field type. Accepts'string','number','boolean','email','phone', or'url'.required(boolean): Marks the field as mandatory.min(number): Minimum string length or minimum numeric value.max(number): Maximum string length or maximum numeric value.pattern(RegExp): Regex the value must match.patternMessage(string): Error message shown when the pattern check fails.custom(function): A custom synchronous validation function that returns a boolean.customMessage(string): Error message shown when the custom function returns false.message(string): Custom error for a failedrequiredcheck on this specific field.
API Methods:
// --- rules object: static boolean checks ---
// Returns true if the value is a valid email address.
rules.email('contact@site.com');
// Returns true for international phone number formats.
rules.phone('+12025551234');
// Returns true if the string is a valid URL.
rules.url('https://mysite.com');
// Returns true for any non-empty, non-null, non-undefined value.
rules.notEmpty('hello');
// Returns true if the value is numeric (including numeric strings).
rules.isNumber(42);
// Returns true for whole integers (including integer strings).
rules.isInt(10);
// Returns true for boolean values or the strings 'true' and 'false'.
rules.isBoolean(true);
// Returns true if the string length is at least n characters.
rules.minLength('admin', 3);
// Returns true if the string length is at most n characters.
rules.maxLength('admin', 20);
// Returns true if the number falls within min and max inclusive.
rules.between(50, 0, 100);
// Returns true if the value matches the given regex.
rules.matches('SKU-001', /^SKU-d+$/);
// Returns true if the value exists in the allowed array.
rules.oneOf('monthly', ['monthly', 'annual', 'trial']);
// Returns true if the password has 8+ characters, uppercase, lowercase, number, and symbol.
rules.strongPassword('Secure@2024');
// Validates postal code format. Second argument is country code: 'US', 'CA', or 'UK'.
rules.postalCode('90210', 'US');
// Validates a credit card number using the Luhn algorithm.
rules.creditCard('4532015112830366');
// Validates an IPv4 or IPv6 address. Pass 4 or 6 as the second argument.
rules.ip('192.168.0.1', 4);
// --- chainable validate() methods ---
// Create a validator and chain rules. Call isValid() or getErrors() at the end.
const v = validate('subscriber@news.com')
.required() // value cannot be empty
.email() // must be a valid email address
.minLength(5) // at least 5 characters
.maxLength(100) // no more than 100 characters
.isNumber() // must be a numeric value
.strongPassword() // meets password complexity rules
.between(1, 999) // number falls within range
.matches(/regex/) // value matches a regex pattern
.oneOf(['a', 'b']) // value is in the list
.phone() // valid phone number format
.url() // valid URL format
.custom((val) => val.includes('@'), 'Must contain @') // custom sync rule
.pipe(val => val.trim()) // transform value before validation
.if(() => true, (v) => v.strongPassword()) // conditional rule when predicate is true
.unless(() => false, (v) => v.minLength(3)); // conditional rule when predicate is false
v.isValid(); // returns true or false
v.getErrors(); // returns string[] of all error messages
v.getValue(); // returns the current value after pipeline transforms
// --- schema() ---
// Returns an object with a .validate(data) method for flat object validation.
const mySchema = schema({
fieldName: { type: 'email', required: true, min: 5, max: 100 }
});
const { valid, errors } = mySchema.validate({ fieldName: 'admin@site.com' });
// --- nestedSchema() ---
// Validates nested objects and returns dot-notated error keys.
const myNestedSchema = nestedSchema({
address: { city: { type: 'string', required: true } }
});
const { valid: nValid, errors: nErrors } = myNestedSchema.validate({
address: { city: '' }
});
// --- nestedSchema().partial() ---
// Validates only the supplied fields; required constraints do not apply to omitted ones.
const partialResult = myNestedSchema.partial({ address: { city: 'Denver' } });
// --- validateAsync() ---
// Accepts a value and a rule-building callback. Returns a resolved promise.
const { valid: aValid, errors: aErrors } = await validateAsync(
'subscribe@news.com',
(v) => v.required().email()
);
// --- batchValidate() ---
// Accepts an array of { field, value, rules } objects and returns per-field results.
const batchResults = batchValidate([
{ field: 'email', value: 'a@b.com', rules: (v) => v.email() }
]);
// --- batchResultSummary() ---
// Takes batchValidate output and returns { allValid, validCount, invalidCount, errors }.
const summary = batchResultSummary(batchResults);
// --- createPipeline() ---
// Chains transforms and validations, then executes them and resolves the result.
const pipeResult = await createPipeline(' RAW INPUT ')
.pipe(v => v.trim())
.validate(v => v.required())
.execute();
// --- benchmark() ---
// Runs a function n times and returns performance metrics.
const bm = benchmark(() => validate('x@y.com').email().isValid(), 10000, 'email-test');
console.log(bm.opsPerSecond); // operations per second
console.log(bm.averageTimeMs); // average time per operation in milliseconds
Alternatives:
- Discover More Free Form Validation Libraries In Pure JavaScript
- 10 Best Pure JavaScript Form Validation Libraries
- Client-Side Declarative Form Validation using Strictly.js
- Custom HTML5 Form Validator In Vanilla JavaScript – Just-validate
- Beginner-Friendly JavaScript Form Validation Library – Trivule
FAQs:
Q: Does it replace native HTML5 form validation?
A: It can replace or supplement native form validation. It provides custom messages, schema validation, async checks, nested object validation, and structured error output.
Q: Why does my async rule not affect isValid()?
A: Async rules need async execution. Use isValidAsync(), validateAsync(), createAsyncValidator().validate(), or batchValidateAsync() when a rule returns a promise.
Q: Should I use schemas or chainable validators?
A: Use chainable validators for single fields and quick checks. Use schemas for full objects, request bodies, settings panels, and any form that needs field-keyed errors.
Q: Why are required fields failing during an edit form update?
A: A full nested schema expects required fields. Use partial() when the form submits only changed fields.
The post Zero-Dependency JavaScript/TypeScript Validation Library – @incogdev/validate appeared first on CSS Script.
Discover more from RSS Feeds Cloud
Subscribe to get the latest posts sent to your email.
