A powerful TypeScript schema validation with advanced conditional logic, cross-field validation, and static type inference.
npm install dynz[![License: MIT][license-image]][license-url]
[![CI][ci-image]][ci-url]
[![NPM version][npm-image]][npm-url]
[![Downloads][downloads-image]][npm-url]
A powerful TypeScript schema validation library with advanced conditional logic, cross-field validation, and privacy controls.
š Type-Safe Validation - Full TypeScript support with strong typing and inference
š Conditional Logic - Dynamic validation rules based on field values
šÆ Cross-Field Validation - Reference other fields in validation rules
š Privacy Controls - Built-in field masking for sensitive data
ā” Performance Optimized - Efficient validation with minimal overhead
š§© Framework Agnostic - Works with any JS frameworks
``bash`
npm install dynzor
pnpm add dynzor
yarn add dynz
`typescript
import { object, string, number, validate, eq, min, email } from "dynz";
// Define a schema
const userSchema = object({
fields: {
name: string({ rules: [min(2)] }),
email: string({ rules: [email()] }),
age: number({ rules: [min(18)] }),
},
});
// Validate data
const result = validate(userSchema, undefined, {
name: "John Doe",
email: "john@example.com",
age: 25,
});
if (result.success) {
console.log("Valid data:", result.values);
} else {
console.log("Validation errors:", result.errors);
}
`
dynz supports comprehensive schema types for all common data structures:
`typescript
import { string, number, boolean, object, array, max, min, regex } from "dynz";
// String schema with validation rules
const nameSchema = string({
rules: [min(2), max(50), regex("^[a-zA-Z\\s]+$")],
});
// Number schema with constraints
const ageSchema = number({
rules: [min(0), max(120)],
});
// Object schema with nested fields
const userSchema = object({
fields: {
profile: object({
fields: {
name: string({ rules: [min(1)] }),
bio: string({ required: false }),
},
}),
},
});
// Array schema
const tagsSchema = array({
schema: string({ rules: [min(1)] }),
rules: [min(1), max(5)],
});
`
Extensive validation rules for precise data validation:
`typescript
import {
object,
string,
min,
max,
email,
number,
regex,
isNumeric,
options,
} from "dynz";
const productSchema = object({
fields: {
name: string({ rules: [min(1), max(100)] }),
email: string({ rules: [email()] }),
price: number({ rules: [min(0)] }),
category: options({ options: ["electronics", "books", "clothing"] }),
sku: string({ rules: [regex("^[A-Z]{3}-\\d{4}$")] }),
quantity: string({ rules: [isNumeric()] }),
},
});
`
dynz excels at dynamic validation based on other field values:
`typescript
import {
eq,
and,
or,
conditional,
email,
min,
object,
oneOf,
regex,
string,
options,
} from "dynz";
const userSchema = object({
fields: {
accountType: options({
options: ["personal", "business"],
}),
// Required only for business accounts
companyName: string({
rules: [min(2)],
required: eq("accountType", "business"),
}),
// Different validation rules based on account type
email: string({
rules: [
email(),
conditional({
when: eq("accountType", "business"),
then: regex(
"@company\\.com$",
"Business accounts must use company email",
),
}),
],
}),
// Complex conditional logic
specialField: string({
required: and(
eq("accountType", "business"),
or(eq("industry", "finance"), eq("industry", "healthcare")),
),
}),
},
});
`
Reference other fields in validation rules:
`typescript
import {
after,
dateString,
equals,
max,
min,
object,
ref,
string,
validate,
} from "dynz";
const signupSchema = object({
fields: {
password: string({ rules: [min(8)] }),
confirmPassword: string({
rules: [equals(ref("password"), "Passwords must match")],
}),
birthYear: dateString({
format: "yyyy",
rules: [min("1900"), max("2024")],
}),
graduatedAt: dateString({
format: "yyyy",
rules: [
after(ref("birthYear"), "Graduation date must be after birth year"),
],
}),
},
});
`
Control when fields can be modified based on conditions:
`typescript
function buildSchema(user: { role: "admin" | "user" }) {
return object({
fields: {
status: options({
options: ["draft", "published"],
}),
title: string({
rules: [min(1)],
mutable: user.role === "admin",
}),
content: string({
mutable: eq("status", "draft"),
}),
createdAt: string({
mutable: false, // Never mutable
}),
},
});
}
`
#### Mutability on array schemas
You can also control mutability on inner schemas of an array. This allows you to add new elements or remove elements, but not to mutate elements on the same index.
`typescript
const schema = array({
// The array is mutable
schema: string({
mutable: false, // The inner schema is immutable
}),
});
validate(schema, [], ["foo"]); // Validates successfully, because it's a new entry
validate(schema, ["foo"], []); // Validates successfully, because an entry is removed
validate(schema, ["foo"], ["bar"]); // Returns an error since 'foo' is mutated into 'bar'
`
Dynamically include or exclude fields:
`typescript
const registrationSchema = object({
fields: {
dietryRestrictions: boolean(),
dietryDetail: string({
included: eq("dietryRestrictions", true),
}),
},
});
`
Create reusable custom validation logic:
`typescript
const passwordStrengthRule = custom('passwordStrength', {
minScore: 4,
requireSpecialChars: true
})
const passwordStrengthRuleValidator: CustomRuleFunction = (value, params) => {
...
}
const strongPasswordSchema = string({
rules: [min(8), passwordStrengthRule]
})
validate(strongPasswordSchema, undefined, 'myStrongPassword', {
customRules: {
passwordStrength: passwordStrengthRuleValidator
}
})
`
`typescript
const orderSchema = object({
fields: {
orderType: options({ options: ["standard", "express", "international"] }),
shippingMethod: options({
options: ["overnight", "same-day", "air", "sea"],
rules: [
conditional({
when: eq("orderType", "express"),
then: oneOf(["overnight", "same-day"]),
}),
conditional({
when: eq("orderType", "international"),
then: oneOf(["air", "sea"]),
}),
],
required: or(
eq("orderType", "express"),
eq("orderType", "international"),
),
}),
customsInfo: object({
fields: {
value: number({ rules: [min(0)] }),
description: string({ rules: [min(1)] }),
},
included: eq("orderType", "international"),
}),
},
});
`
`typescript
// Order management with status-based mutability
const orderSchema = object({
fields: {
orderStatus: options({
options: ["draft", "pending", "send"],
// always immutable
mutable: false,
}),
items: array({
schema: string(),
mutable: or(eq("orderStatus", "draft"), eq("orderStatus", "pending")),
}),
shippingAddress: string({
mutable: and(
or(eq("orderStatus", "draft"), eq("orderStatus", "pending")),
),
}),
},
});
`
- string(options?) - String validation schemanumber(options?)
- - Number validation schemaboolean(options?)
- - Boolean validation schemaobject({ fields })
- - Object schema with nested fieldsarray({ schema })
- - Array schema with item validationdateString(options?)
- - Date string validation with format supportoptions({ options })
- - Enum-like validation for predefined valuesfile(options?)
- - File validation schema
- min(value, message?) - Minimum value/lengthmax(value, message?)
- - Maximum value/lengthbefore(value, message?)
- - Before value/lengthafter(value, message?)
- - After value/lengthemail(message?)
- - Email format validationregex(pattern, message?)
- - Regular expression validationequals(value, message?)
- - Exact value matchingoneOf(values, message?)
- - Must be one of specified valuesisNumeric(message?)
- - Numeric string validationcustom(name, params?, message?)
- - Custom validation rule
- eq(field, value) - Field equals valueneq(field, value)
- - Field not equals valuegt(field, value)
- - Field greater than valuegte(field, value)
- - Field greater than or equallt(field, value)
- - Field less than valuelte(field, value)
- - Field less than or equalmatches(field, pattern)
- - Field matches regex patternand([...conditions])
- - All conditions must be trueor([...conditions])
- - At least one condition must be true
- validate(schema, currentValues?, newValues, options?) - Main validation functionvalidateMutable
- option - Check field mutability constraints (defaults to true)customRules
- option - Provide custom rule implementations
dynz provides excellent TypeScript integration:
`typescript
import {
array,
min,
number,
object,
string,
validate,
SchemaValues,
} from "dynz";
const schema = object({
fields: {
name: string({ rules: [min(1)] }),
age: number({ required: false }),
tags: array({ schema: string() }),
},
});
// Inferred type: { name: string; age?: number; tags: string[] }
type UserData = SchemaValues
// Type-safe validation results
const result = validate(schema, undefined, {
name: "John",
tags: ["dynz"],
});
if (result.success) {
// result.values is properly typed as UserData
console.log(result.values.name); // ā
Type-safe access
}
`
Check out the /examples directory for complete working examples:
- Next.js Example - React forms with dynz schemas
| Feature | dynz | Zod | Yup | Joi |
| ---------------------- | -------------- | --------- | ---------- | ---------- |
| TypeScript Support | ā
Native | ā
Native | ā ļø Partial | ā Runtime |
| Conditional Validation | ā
Built-in | ā ļø Manual | ā ļø Limited | ā ļø Limited |
| Cross-field References | ā
Native | ā ļø Manual | ā
Native | ā
Native |
| Privacy/Masking | ā
Built-in | ā | ā | ā |
| Mutability Controls | ā
Native | ā | ā | ā |
| Field Inclusion | ā
Conditional | ā | ā | ā |
| Bundle Size | š” Medium | š¢ Small | š” Medium | š“ Large |
`typescript
const loanApplicationSchema = object({
fields: {
applicantType: string({ rules: [oneOf(["individual", "business"])] }),
income: number({
rules: [min(0)],
required: eq("applicantType", "individual"),
}),
businessRevenue: number({
rules: [min(0)],
required: eq("applicantType", "business"),
}),
loanAmount: number({
rules: [
min(1000),
conditional({
when: eq("applicantType", "individual"),
then: max(ref("income")), // Can't exceed annual income
}),
conditional({
when: eq("applicantType", "business"),
then: max(ref("businessRevenue")), // Can't exceed annual revenue
}),
],
}),
},
});
`
`typescript
const documentWorkflowSchema = object({
fields: {
content: string({
mutable: and([
eq("status", "draft"),
or([eq("isOwner", true), eq("hasEditPermission", true)]),
]),
}),
status: string({
rules: [oneOf(["draft", "review", "approved", "published"])],
mutable: or([
and([eq("currentStatus", "draft"), eq("isOwner", true)]),
and([eq("currentStatus", "review"), eq("userRole", "reviewer")]),
eq("userRole", "admin"),
]),
}),
},
});
``
We welcome contributions! Please see our Contributing Guide for details.
MIT Ā© dynz
- More framework integrations coming soon...
---
Built with ā¤ļø for type-safe validation
[license-image]: https://img.shields.io/badge/License-MIT-brightgreen.svg?style=flat-square
[license-url]: https://opensource.org/licenses/MIT
[ci-image]: https://img.shields.io/github/actions/workflow/status/rubenvanrooij/dynz/ci.yml?branch=main&logo=github&style=flat-square
[ci-url]: https://github.com//rubenvanrooij/dynz/actions/workflows/ci.yml
[npm-image]: https://img.shields.io/npm/v/dynz.svg?style=flat-square
[npm-url]: https://npmjs.org/package/dynz
[downloads-image]: https://img.shields.io/npm/dm/dynz.svg?style=flat-square