Simple Decorator based Model Validation Engine
A comprehensive TypeScript library that provides a powerful model validation framework using decorators. It enables type-safe, declarative validation for your TypeScript models with features for serialization, comparison, and hashing. The library offers a rich set of validation decorators for various data types and constraints, making it easy to define and enforce validation rules on your model properties.
Documentation available here
Description
The decorator-validation library is a comprehensive TypeScript implementation of a decorator-based validation system. It provides a robust framework for defining, validating, and managing model objects in TypeScript applications.
Meant to be easily extended, customized, and integrated with browser input validation mechanisms, this library offers a declarative approach to validation through TypeScript decorators.
Core Components
Model System
The library is built around the abstract Model
class, which serves as the foundation for all model objects. The Model system provides:
- A registry mechanism for storing and retrieving model constructors
- Serialization and deserialization capabilities
- Hashing functionality for model objects
- Equality comparison between model objects
- Support for nested model instantiation and validation
Validation Framework
The validation framework offers a rich set of decorators for validating model properties:
- Basic Validation:
required()
,min()
,max()
,step()
,minlength()
,maxlength()
,pattern()
- Type-Specific Validation:
email()
,url()
,type()
,date()
,password()
,list()
,set()
- Comparison Validation:
eq()
,diff()
,lt()
,lte()
,gt()
,gte()
for comparing properties
Key Features
- Decorator-based validation API with recursive validation for nested models
- Customizable Model building factories enabling nested instantiation
- Model serialization/deserialization with configurable serializers
- Model Hashing with configurable algorithms
- Model Equality comparison with support for excluding properties
- Easily extended custom validation through the decorator system
- Java-like date handling (format and serialization)
- Configurable error messages for all validation rules
- Comparative validation between attributes, supporting various comparison operators
- Type safety through TypeScript's static typing system
The library is designed with extensibility in mind, allowing developers to create custom validators and decorators to meet specific application requirements. It integrates seamlessly with TypeScript's type system to provide compile-time type checking alongside runtime validation.
How to Use
Examples
Creating a Model Class
import { Model, model, required, email, minlength, maxlength, min, hashedBy, serializedBy } from 'decorator-validation';
@model()
@hashedBy('sha256')
@serializedBy('json')
class User extends Model {
@required()
@minlength(3)
@maxlength(50)
username!: string;
@required()
@email()
email!: string;
@required()
@min(18, "User must be at least 18 years old")
age!: number;
constructor(data?: any) {
super(data);
Model.fromModel(this, data);
}
}
Basic Validation
// Create a user with invalid data
const invalidUser = new User({
username: "jo", // too short
email: "not-an-email",
age: 16 // below minimum
});
// Check for validation errors
const errors = invalidUser.hasErrors();
console.log(errors);
// Output will contain validation errors for username, email, and age
// Create a valid user
const validUser = new User({
username: "john_doe",
email: "john@example.com",
age: 25
});
// Check for validation errors
const validErrors = validUser.hasErrors();
console.log(validErrors); // undefined - no errors
Using Different Validation Decorators
Numeric Validation
class Product {
@required()
name!: string;
@required()
@min(0, "Price cannot be negative")
@max(10000, "Price cannot exceed 10,000")
@step(0.01, "Price must have at most 2 decimal places")
price!: number;
constructor(data?: any) {
Model.fromModel(this, data);
}
}
String Validation
class Article {
@required()
@minlength(5)
@maxlength(100)
title!: string;
@required()
@minlength(50)
@maxlength(5000)
content!: string;
@pattern(/^[a-z0-9-]+$/, "Slug must contain only lowercase letters, numbers, and hyphens")
slug!: string;
constructor(data?: any) {
Model.fromModel(this, data);
}
}
Special Types Validation
class Contact {
@required()
name!: string;
@required()
@email()
email!: string;
@url()
website?: string;
@date("yyyy-MM-dd")
birthdate?: Date;
@password()
password!: string;
constructor(data?: any) {
Model.fromModel(this, data);
}
}
Comparison Validation
class DateRange {
@required()
@date("yyyy-MM-dd")
startDate!: Date;
@required()
@date("yyyy-MM-dd")
@gt("startDate", "End date must be after start date")
endDate!: Date;
constructor(data?: any) {
Model.fromModel(this, data);
}
}
class PriceRange {
@required()
@min(0)
minPrice!: number;
@required()
@min(0)
@gte("minPrice", "Maximum price must be greater than or equal to minimum price")
maxPrice!: number;
constructor(data?: any) {
Model.fromModel(this, data);
}
}
Collection Validation
class Tag {
@required()
name!: string;
constructor(data?: any) {
Model.fromModel(this, data);
}
}
class BlogPost {
@required()
title!: string;
@required()
content!: string;
@list(Tag)
tags!: Tag[];
@set(Tag)
uniqueTags!: Set<Tag>;
constructor(data?: any) {
Model.fromModel(this, data);
}
}
Model Registry and Building
// Register models
Model.register(User);
Model.register(BlogPost);
Model.register(Tag);
// Build a model from plain object
const userData = {
username: "jane_doe",
email: "jane@example.com",
age: 28
};
const user = Model.build(userData, "User");
// Bulk register models
bulkModelRegister(User, BlogPost, Tag);
Serialization and Deserialization
// Create a user
const user = new User({
username: "john_doe",
email: "john@example.com",
age: 25
});
// Serialize to string
const serialized = user.serialize();
console.log(serialized);
// Output: JSON string representation of the user
// Deserialize from string
const deserialized = Model.deserialize(serialized);
console.log(deserialized);
// Output: User object with the same properties
Comparing Models
const user1 = new User({
username: "john_doe",
email: "john@example.com",
age: 25
});
const user2 = new User({
username: "john_doe",
email: "john@example.com",
age: 25
});
const user3 = new User({
username: "jane_doe",
email: "jane@example.com",
age: 28
});
console.log(user1.equals(user2)); // true - same properties
console.log(user1.equals(user3)); // false - different properties
// Compare ignoring specific properties
console.log(user1.equals(user3, "username", "email")); // true - only comparing age
Hashing Models
const user = new User({
username: "john_doe",
email: "john@example.com",
age: 25
});
// Get hash of the model
const hash = user.hash();
console.log(hash);
// Output: Hash string based on the configured algorithm (sha256)
Related
Social
Languages
Getting help
If you have bug reports, questions or suggestions please create a new issue.
Contributing
I am grateful for any contributions made to this project. Please read this to get started.
Supporting
The first and easiest way you can support it is by Contributing. Even just finding a typo in the documentation is important.
Financial support is always welcome and helps keep both me and the project alive and healthy.
So if you can, if this project in any way. either by learning something or simply by helping you save precious time, please consider donating.
License
This project is released under the MIT License.
By developers, for developers...