π Why Data Validation Matters
Data validation is a crucial part of modern web applications, ensuring data integrity, security, and a smooth user experience. However, developers often struggle with:
- Writing separate validation logic for frontend and backend
- Managing multiple validation libraries across environments
- Keeping validation consistent between client and server
Introducing IsoValidβa powerful, isomorphic validation library that allows you to use the same validation logic across both browser and Node.js environments.
π₯ What is IsoValid?
IsoValid is a lightweight, TypeScript-first validation library designed to unify validation across your entire application stack. Whether you're validating user input in a React form or ensuring API request integrity in an Express server, IsoValid makes it seamless.
π Key Features
β
True Isomorphic Support β The same validation code runs in both frontend and backend
β
TypeScript-First β Fully typed API for strong type inference
β
Minimal Bundle Size β No unnecessary dependencies or bloat
β
Intuitive API β Developer-friendly, chainable syntax
β
High Performance β Optimized validation with minimal overhead
β
Extensible β Easily add custom validators and error messages
π¦ Installation
Get started with IsoValid in seconds:
npm install isovalid
πΉ Core Architecture
IsoValid is built on a flexible, extensible architecture that includes:
Base Schema Class
- Foundation for all validation schemas
- Handles optional/nullable states
- Common validation logic across types
Type-Specific Schemas
-
StringSchema
β Length, regex, email, trimming, etc. -
NumberSchema
β Min/max, integer, positive/negative checks - More types coming soon (Boolean, Array, Object)
Validation Pipeline
- Multi-stage validation process
- Supports custom validation functions
- Returns detailed error reporting
β‘ Usage Examples
String Validation
import { v } from 'isovalid';
const stringSchema = v.string()
.min(2) // Minimum length
.max(50) // Maximum length
.email() // Must be a valid email
.matches(/regex/) // Custom pattern
.trimmed() // Remove extra whitespace
.setOptional() // Allow undefined
.setNullable(); // Allow null
Number Validation
const numberSchema = v.number()
.min(0) // Minimum value
.max(100) // Maximum value
.integer() // Must be an integer
.positive(); // Must be > 0
Validation Results
interface ValidationResult {
valid: boolean;
errors: Array<{ path: string[]; message: string; }>;
}
π― Real-World Examples
1. React Form Validation
import { v } from 'isovalid';
import { useState, FormEvent } from 'react';
const userSchema = {
username: v.string().min(3).max(20),
email: v.string().email(),
age: v.number().integer().min(18)
};
function RegistrationForm() {
const [formData, setFormData] = useState({ username: '', email: '', age: '' });
const [errors, setErrors] = useState<Record<string, string>>({});
const validateField = (field: keyof typeof userSchema, value: any) => {
const result = userSchema[field].validate(value);
return result.valid ? null : result.errors[0].message;
};
const handleSubmit = (e: FormEvent) => {
e.preventDefault();
const newErrors: Record<string, string> = {};
Object.entries(formData).forEach(([field, value]) => {
const error = validateField(field as keyof typeof userSchema, value);
if (error) newErrors[field] = error;
});
if (Object.keys(newErrors).length === 0) {
console.log('Submitting:', formData);
} else {
setErrors(newErrors);
}
};
return (
<form onSubmit={handleSubmit}>
<input type="text" placeholder="Username" onChange={e => setFormData(prev => ({ ...prev, username: e.target.value }))} />
{errors.username && <span>{errors.username}</span>}
<button type="submit">Register</button>
</form>
);
}
2. Express API Validation
import express from 'express';
import { v } from 'isovalid';
const app = express();
app.use(express.json());
const productSchema = {
name: v.string().min(3).max(100),
price: v.number().min(0),
category: v.string().custom(value =>
['electronics', 'books', 'clothing'].includes(value) ? null : 'Invalid category'
)
};
app.post('/api/products', (req, res) => {
const errors = Object.entries(productSchema)
.map(([field, schema]) => ({ field, result: schema.validate(req.body[field]) }))
.filter(({ result }) => !result.valid)
.map(({ field, result }) => ({ field, message: result.errors[0].message }));
if (errors.length > 0) {
return res.status(400).json({ errors });
}
res.status(201).json(req.body);
});
π― Best Practices
β
Schema Reuse β Define schemas once & share across frontend/backend
β
Type Safety β Leverage TypeScriptβs inference capabilities
β
Performance Optimization β Cache schema instances for reuse
β
Error Handling β Always check .valid
before using data
π οΈ Roadmap
π Upcoming Features:
- [ ] Array & Object Schema Support
- [ ] Custom Error Message Templates
- [ ] Async Validation
- [ ] Integration with Popular Form Libraries
- [ ] Schema Composition & Inheritance
π Get Started with IsoValid
IsoValid is open-source and welcomes contributions! Try it today and share your feedback.
π NPM Package: IsoValid on NPM
π GitHub Repository: GitHub Repo
Letβs make data validation simpler together! π
Top comments (0)