| name | dhi-typescript |
| description | Ultra-fast validation library for TypeScript/JavaScript (77x faster than Zod). Use when building validated schemas for APIs, forms, or data processing. Provides Zod 4-compatible API with WASM-powered SIMD validation. |
| license | MIT |
| metadata | {"dependencies":"dhi","npm":"https://www.npmjs.com/package/dhi","github":"https://github.com/justrach/dhi"} |
dhi - Ultra-Fast TypeScript Validation
Overview
dhi is a high-performance validation library for TypeScript and JavaScript, powered by Zig-compiled WebAssembly with SIMD optimizations. It provides a Zod 4-compatible API while being 77x faster for validation operations.
Use dhi when you need:
- Fast schema validation for APIs
- Form validation in frontend apps
- Data parsing and transformation
- Type-safe runtime validation
Installation
npm install dhi
bun add dhi
pnpm add dhi
Quick Start
Basic Schema
import { z } from 'dhi';
const UserSchema = z.object({
name: z.string().min(1).max(100),
age: z.number().int().min(0).max(120),
email: z.string().email(),
score: z.number().default(0),
});
type User = z.infer<typeof UserSchema>;
const user = UserSchema.parse({
name: "Alice",
age: 25,
email: "alice@example.com"
});
Safe Parsing
const result = UserSchema.safeParse(data);
if (result.success) {
console.log(result.data);
} else {
console.log(result.error.issues);
}
Schema Types
Primitives
z.string()
z.number()
z.boolean()
z.bigint()
z.date()
z.undefined()
z.null()
z.void()
z.any()
z.unknown()
z.never()
String Validations
z.string()
.min(1)
.max(100)
.length(10)
.email()
.url()
.uuid()
.cuid()
.cuid2()
.ulid()
.regex(/pattern/)
.includes("text")
.startsWith("pre")
.endsWith("suf")
.datetime()
.ip()
.trim()
.toLowerCase()
.toUpperCase()
Number Validations
z.number()
.int()
.positive()
.nonnegative()
.negative()
.nonpositive()
.min(0)
.max(100)
.gt(0)
.lt(100)
.multipleOf(5)
.finite()
.safe()
Objects
const PersonSchema = z.object({
name: z.string(),
age: z.number(),
});
const PartialPerson = PersonSchema.partial();
const RequiredPerson = PersonSchema.required();
const NameOnly = PersonSchema.pick({ name: true });
const NoAge = PersonSchema.omit({ age: true });
const Employee = PersonSchema.extend({
employeeId: z.string(),
});
const Combined = PersonSchema.merge(AddressSchema);
const StrictPerson = PersonSchema.strict();
const LoosePerson = PersonSchema.passthrough();
Arrays
z.array(z.string())
.min(1)
.max(10)
.length(5)
.nonempty()
z.tuple([z.string(), z.number()])
Unions and Intersections
const StringOrNumber = z.union([z.string(), z.number()]);
const Event = z.discriminatedUnion("type", [
z.object({ type: z.literal("click"), x: z.number(), y: z.number() }),
z.object({ type: z.literal("scroll"), offset: z.number() }),
]);
const Combined = z.intersection(SchemaA, SchemaB);
Enums and Literals
const Status = z.enum(["pending", "active", "archived"]);
enum Direction { Up, Down, Left, Right }
const DirectionSchema = z.nativeEnum(Direction);
const One = z.literal(1);
const Hello = z.literal("hello");
Optional and Nullable
z.string().optional()
z.string().nullable()
z.string().nullish()
Transformations
const Trimmed = z.string().transform(s => s.trim());
z.coerce.string()
z.coerce.number()
z.coerce.boolean()
z.coerce.date()
const Schema = z.preprocess(
(val) => String(val).trim(),
z.string()
);
const Pipeline = z.string()
.transform(s => s.split(","))
.pipe(z.array(z.string()));
Records and Maps
z.record(z.string())
z.record(z.string(), z.number())
z.map(z.string(), z.number())
Refinements
const EvenNumber = z.number().refine(
n => n % 2 === 0,
{ message: "Must be even" }
);
const PasswordSchema = z.object({
password: z.string(),
confirm: z.string(),
}).superRefine((data, ctx) => {
if (data.password !== data.confirm) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: "Passwords don't match",
path: ["confirm"],
});
}
});
Error Handling
try {
UserSchema.parse(invalidData);
} catch (error) {
if (error instanceof z.ZodError) {
console.log(error.issues);
console.log(error.format());
console.log(error.flatten());
}
}
Type Inference
type UserInput = z.input<typeof UserSchema>;
type UserOutput = z.output<typeof UserSchema>;
type User = z.infer<typeof UserSchema>;
Performance
dhi is 77x faster than Zod for validation operations:
| Operation | dhi | Zod | Speedup |
|---|
| String formats | 46M/sec | 0.6M/sec | 77x |
| Object validation | Fast | Slower | ~50x |
| Array validation | Fast | Slower | ~40x |
Next.js Integration
import { z } from 'dhi';
import { NextResponse } from 'next/server';
const CreateUserSchema = z.object({
name: z.string().min(1),
email: z.string().email(),
});
export async function POST(request: Request) {
const body = await request.json();
const result = CreateUserSchema.safeParse(body);
if (!result.success) {
return NextResponse.json(
{ error: result.error.flatten() },
{ status: 400 }
);
}
return NextResponse.json({ user: result.data });
}
Edge Runtime Compatible
dhi works in edge runtimes (Vercel Edge, Cloudflare Workers) with a tiny 28KB WASM bundle.
export const runtime = 'edge';
import { z } from 'dhi';
Migration from Zod
dhi is designed as a drop-in replacement:
import { z } from 'zod';
import { z } from 'dhi';
Most Zod 4 code works unchanged with dhi.
When to Use
Use dhi when:
- Building high-performance APIs
- Need fast form validation
- Working with edge runtimes
- Processing large volumes of data
- Need Zod compatibility with better performance
Resources