Home

Banner

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.

Licence GitHub language count GitHub top language

Build & Test CodeQLSnyk Analysis Pages builder .github/workflows/release-on-tag.yaml

Open Issues Closed Issues Pull Requests Maintained

Forks Stars Watchers

Node Version NPM Version

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

decaf-ts Reflection

Social

LinkedIn

Languages

TypeScript JavaScript NodeJS ShellScript

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...