zod-rs
A Rust implementation inspired by Zod for schema validation
zod-rs is a TypeScript-first schema validation library with static type inference, inspired by Zod. It provides a simple and intuitive API for validating JSON data with comprehensive error reporting.
Features
- Type-safe validation - Full type safety with compile-time guarantees
- Zero dependencies - Lightweight core with optional integrations
- Rich error messages - Detailed validation errors with path information
- Composable schemas - Build complex validation rules from simple primitives
- Framework integration - Built-in support for Axum and other web frameworks
- High performance - Efficient validation with minimal overhead
- Developer friendly - Intuitive API similar to TypeScript Zod
- Schema inference - Automatically generate schemas from Rust structs and enums
- Attribute macros - Rich validation constraints via
#[zod(...)]attributes - Validator replacement - Drop-in replacement for the
validatorcrate - Internationalization (i18n) — Localized error messages and validation feedback
- Enum support - Full enum validation with unit, tuple, and struct variants
- TypeScript codegen - Generate TypeScript Zod schemas from Rust types
Installation
Add zod-rs to your Cargo.toml:
[]
= "1.0"
# Optional: for web framework integration
= { = "1.0", = ["axum"] }
# For TypeScript Zod schema generation
= { = "1.0", = ["ts"] }
# Or use the standalone crate
= "1.0"
# For schema derivation from structs (recommended)
= "1.0"
= { = "1.0", = ["derive"] }
= "1.0"
Quick Start
use json;
use *;
API Reference
Basic Types
String Validation
use *;
use json;
// Basic string
let schema = string;
assert!;
// String with length constraints
let schema = string.min.max;
assert!;
assert!;
// Exact length
let schema = string.length;
assert!;
// Pattern matching
let schema = string.regex;
assert!;
assert!;
// Email validation
let schema = string.email;
assert!;
// URL validation
let schema = string.url;
assert!;
Number Validation
use *;
use json;
// Basic number
let schema = number;
assert!;
// Integer only
let schema = number.int;
assert!;
assert!;
// Range constraints
let schema = number.min.max;
assert!;
assert!;
// Positive numbers
let schema = number.positive;
assert!;
assert!;
// Non-negative numbers
let schema = number.nonnegative;
assert!;
assert!;
// Finite numbers (excludes NaN, Infinity)
let schema = number.finite;
assert!;
Boolean Validation
use *;
use json;
let schema = boolean;
assert!;
assert!;
assert!;
Literal Validation
use *;
use json;
// String literal
let schema = literal;
assert!;
assert!;
// Number literal
let schema = literal;
assert!;
assert!;
Complex Types
Array Validation
use *;
use json;
// Array of strings
let schema = array;
assert!;
// Array with length constraints
let schema = array.min.max;
assert!;
assert!;
// Array with exact length
let schema = array.length;
assert!;
// Nested arrays
let schema = array;
assert!;
Object Validation
use *;
use json;
// Simple object
let schema = object
.field
.field;
let data = json!;
assert!;
// Object with optional fields
let schema = object
.field
.optional_field;
let data = json!;
assert!;
// Strict mode (no additional properties)
let schema = object
.field
.strict;
Optional Values
use *;
use json;
let schema = optional;
assert!;
assert!;
// Method chaining
let schema = string.optional;
Union Types
use *;
use json;
let schema = union
.variant
.variant;
assert!;
assert!;
assert!;
// Literal unions (enums)
let schema = union
.variant
.variant
.variant;
Schema Methods
All schemas support these methods:
parse(value) - Parse with panic on error
let schema = string;
let result = schema.parse; // Panics on validation failure
safe_parse(value) - Parse with Result
let schema = string;
match schema.safe_parse
validate(value) - Alias for safe_parse
let schema = string;
let result = schema.validate;
Complex Examples
Struct Validation
use ;
use json;
use *;
Nested Objects
use *;
use json;
let user_data = json!;
assert!;
Web Framework Integration
Axum Integration
Enable the axum feature in your Cargo.toml:
[]
= { = "1.0", = ["axum"] }
= "0.7"
= { = "1.0", = ["full"] }
= { = "1.0", = ["derive"] }
= "1.0"
use ;
use ;
use Value;
use *;
async
async
Error Handling
zod-rs provides detailed error information with path tracking:
use *;
use json;
let schema = object
.field;
let invalid_data = json!;
match schema.safe_parse
Error Types
ValidationError::Required- Missing required fieldValidationError::InvalidType- Wrong data typeValidationError::InvalidValue- Provided value does not match the expected valueValidationError::InvalidValues- Provided value does not match any of the expected values.ValidationError::TooSmall/TooBig- Value or lenght out of range (String, Number, Array ... etc)ValidationError::InvalidFormat- String format validation (starts with , ends with, includes, regex, ... etc)ValidationError::InvalidNumber- Invalid number constraint (finite, positive, ... etc)ValidationError::UnrecognizedKeys- Object with unrecognized keysValidationError::InvalidUnion- No union matchingValidationError::Custom- Custom validation errors
Internationalization (i18n)
zod-rs comes with built-in locale support so you can get validation errors in different languages.
Currently supported
- English (default)
- Arabic
Example
use json;
use *;
let login_schema = object
.field
.field
.strict;
let input = json!;
match login_schema.safe_parse
Want to add a new language? Missing a translation? Open an issue or PR on GitHub — contributions are welcome.
Advanced Usage
Schema Inference from Structs
zod-rs provides a powerful derive macro that automatically generates validation schemas from Rust structs, making it an excellent replacement for the validator crate.
use ;
use json;
use *;
let user_data = json!;
match validate_and_parse
let schema = schema;
match schema.validate
let user_from_json = from_json?;
Available Validation Attributes
The #[zod(...)] attribute supports the following constraints:
String Validation:
min_length(n)- Minimum string lengthmax_length(n)- Maximum string lengthstarts_with("value")- String starts with a given valueends_with("value")- String ends with a given valueincludes("value")- String includes a given valuelength(n)- Exact string lengthemail- Email format validationurl- URL format validationregex("pattern")- Regular expression pattern matching
Number Validation:
min(n)- Minimum valuemax(n)- Maximum valueint- Integer only (no decimals)positive- Must be positive (> 0)negative- Must be negative (< 0)nonnegative- Must be non-negative (>= 0)nonpositive- Must be non-positive (<= 0)finite- Must be finite (excludes NaN, Infinity)
Array Validation:
min_length(n)- Minimum array lengthmax_length(n)- Maximum array lengthlength(n)- Exact array length
Nested Structs
The derive macro automatically handles nested structs:
Generated Methods
The ZodSchema derive macro generates the following methods:
schema()- Returns the validation schemavalidate_and_parse(value)- Validates and deserializes JSON valuefrom_json(json_str)- Validates and parses from JSON stringvalidate_json(json_str)- Validates JSON string (returns Value)
Enum Support
zod-rs fully supports Rust enums with the ZodSchema derive macro. Enums are validated using the externally-tagged format (serde default).
use ;
use json;
use *;
// Unit variants
// Tuple variants
// Struct variants
// Mixed variants
JSON Format for Enum Variants
| Variant Type | Rust | JSON |
|---|---|---|
| Unit | Status::Active |
{"Active": null} |
| Tuple (single) | Message::Text("hi") |
{"Text": "hi"} |
| Tuple (multiple) | Message::Coords(1, 2) |
{"Coords": [1, 2]} |
| Struct | Event::Click { x: 1, y: 2 } |
{"Click": {"x": 1, "y": 2}} |
TypeScript Zod Schema Generation
Generate TypeScript Zod schemas from your Rust types using the ZodTs derive macro.
use ZodTs;
Generated TypeScript:
import * as z from "zod";
export const UserSchema = z.object({
username: z.string().min(2).max(50),
email: z.string().email(),
age: z.number().int().min(18).max(120),
bio: z.string().optional()
});
export type User = z.infer<typeof UserSchema>;
Zod Version
The generated schemas target Zod v4 by default (import * as z from "zod"). To emit Zod v3 style imports instead, enable the zod-v3 feature:
[]
= { = "...", = ["ts", "zod-v3"] }
# or, if depending on zod-rs-ts directly:
= { = "...", = ["zod-v3"] }
When using the CLI, pass --zod-version v3 to switch (defaults to v4):
Standard Schema Compatibility
Schemas produced by ZodTs are Standard Schema compliant out of the box — this comes from Zod itself (Zod v3.24+ and all Zod v4 schemas implement the ~standard interface natively). That means the generated output works directly with Standard Schema consumers like TanStack Form, React Hook Form, and other validation-library-agnostic tooling, with no extra wiring required.
Enum TypeScript Generation
Generated TypeScript:
export const StatusSchema = z.union([
z.object({ Active: z.null() }),
z.object({ Inactive: z.null() })
]);
export const EventSchema = z.union([
z.object({ Click: z.object({ x: z.number().int(), y: z.number().int() }) }),
z.object({ Scroll: z.object({ delta: z.number() }) })
]);
CLI Tool
Install and use the CLI for batch generation:
# Install with CLI feature
# Generate schemas from Rust source files (emits Zod v4 imports by default)
# Generate all schemas in a single file
# Target Zod v3 imports instead
Custom Validation
use *;
use ;
use Value;
let schema = CustomSchema ;
assert!;
assert!;
Schema Composition
use *;
Testing
Run the test suite:
Run examples:
# Basic usage
# Struct validation
# Derive macro for schema inference
# Validator crate replacement
# TypeScript Zod schema generation
# Axum integration
Workspace Structure
This project uses a Cargo workspace with the following crates:
zod-rs- Main validation library with schema typeszod-rs-macros- Derive macros forZodSchemazod-rs-ts- TypeScript Zod schema generationzod-rs-util- Utility functions, error handling and i18n
Contributing
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
Development Setup
- Clone the repository
- Run tests:
cargo test - Run examples:
cargo run --example basic_usage - Format code:
cargo fmt - Check with clippy:
cargo clippy
License
This project is licensed under either of
- Apache License, Version 2.0, (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.
Acknowledgments
- Inspired by Zod by Colin McDonnell
- Built for the Rust community
Related Projects
- Zod - TypeScript-first schema validation
- Serde - Rust serialization framework
- Validator - Rust struct validation
zod-rs vs Validator Crate
zod-rs provides significant advantages over the traditional validator crate:
| Feature | zod-rs | validator crate |
|---|---|---|
| Schema Definition | Derive macro with attributes | Struct attributes only |
| Runtime Flexibility | Dynamic schema creation | Compile-time only |
| Error Messages | Detailed with full path context | Basic field-level errors |
| JSON Integration | Built-in JSON validation/parsing | Manual serde integration |
| Nested Validation | Automatic nested struct support | Manual implementation |
| Schema Reuse | Composable and reusable schemas | Struct-bound validation |
| Type Safety | Full type inference | Limited type information |
| Performance | Optimized validation pipeline | Direct field validation |
| Extensibility | Custom validators and schemas | Custom validation functions |
| Framework Integration | Built-in web framework support | Manual integration required |
| Internationalization | Built-in Localized error messages | No i18n support |
Migration from Validator Crate
// Before: using validator crate
use ;
// After: using zod-rs
use *;
// Enhanced capabilities with zod-rs
let user_data = json!;
// Validate and parse in one step
let user = validate_and_parse?;
// Or validate JSON string directly
let user = from_json?;
// Reuse schema for different purposes
let schema = schema;
let is_valid = schema.validate.is_ok;
Made by Maulana Sodiqin