npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

validor

v1.0.1

Published

A lightweight and extensible TypeScript-first validation library with schema-based validation for Node.js, React, Express, and NestJS.

Downloads

217

Readme

Validor

A lightweight and extensible TypeScript-first validation library with schema-based validation for Node.js, React, Express, and NestJS.

TypeScript License: MIT

Features

  • 🔷 TypeScript-first: Full type safety and inference
  • 🎯 Schema-based validation: Declarative and composable schemas
  • 🌍 i18n support: Built-in internationalization (English, Uzbek, Russian, Karakalpak)
  • Lightweight: Zero dependencies, small bundle size
  • 🔗 Chainable API: Fluent and intuitive validation chains
  • 🎨 Extensible: Easy to customize and extend
  • 🛡️ Type-safe: Automatic type inference from schemas
  • Comprehensive: String, Number, Date, Boolean, Object, Array, IPv4 validation

Installation

npm install validor
yarn add validor
pnpm add validor

Quick Start

import { string, number, object } from "validor";

// Define a schema
const userSchema = object({
  name: string().min(2).max(50),
  email: string().email(),
  age: number().min(18).max(120),
});

// Validate data
try {
  const user = userSchema.parse({
    name: "John Doe",
    email: "[email protected]",
    age: 25,
  });
  console.log("Valid user:", user);
} catch (error) {
  console.error("Validation error:", error.message);
}

Usage

String Validation

import { string } from "validor";

// Basic string validation
const schema = string();
schema.parse("hello"); // ✓ 'hello'

// Required string
const required = string().required();
required.parse(""); // ✗ ValidationError

// Length constraints
const username = string().min(3).max(20);
username.parse("john"); // ✓ 'john'

// Email validation
const email = string().email();
email.parse("[email protected]"); // ✓ '[email protected]'

// URL validation
const url = string().url();
url.parse("https://example.com"); // ✓ 'https://example.com'

// Pattern matching
const phoneNumber = string().regex(/^\+998\d{9}$/);
phoneNumber.parse("+998901234567"); // ✓ '+998901234567'

// String transformations
const normalized = string().trim().lowercase();
normalized.parse("  HELLO  "); // ✓ 'hello'

// Non-empty strings
const nonempty = string().nonempty();
nonempty.parse(""); // ✗ ValidationError

// Exact length
const pinCode = string().length(4);
pinCode.parse("1234"); // ✓ '1234'

Number Validation

import { number } from "validor";

// Basic number validation
const schema = number();
schema.parse(42); // ✓ 42

// Range validation
const age = number().min(0).max(120);
age.parse(25); // ✓ 25

// Positive/Negative numbers
const positive = number().positive();
positive.parse(10); // ✓ 10

const negative = number().negative();
negative.parse(-5); // ✓ -5

// Non-negative (>= 0)
const nonnegative = number().nonnegative();
nonnegative.parse(0); // ✓ 0

// Integer validation
const integer = number().integer();
integer.parse(42); // ✓ 42
integer.parse(42.5); // ✗ ValidationError

// Finite numbers only
const finite = number().finite();
finite.parse(100); // ✓ 100
finite.parse(Infinity); // ✗ ValidationError

// Multiple of
const evenNumber = number().multipleOf(2);
evenNumber.parse(10); // ✓ 10

// Range check
const percentage = number().between(0, 100);
percentage.parse(50); // ✓ 50

Date Validation

import { date } from "validor";

// Basic date validation
const schema = date();
schema.parse(new Date()); // ✓ Date object

// Date can be string, number, or Date object
schema.parse("2024-01-01"); // ✓ Date object
schema.parse(1704067200000); // ✓ Date object

// Minimum date
const futureDate = date().min(new Date("2024-01-01"));
futureDate.parse(new Date("2024-06-01")); // ✓

// Maximum date
const pastDate = date().max(new Date("2024-12-31"));
pastDate.parse(new Date("2024-06-01")); // ✓

// Must be in the past
const birthDate = date().past();
birthDate.parse(new Date("2000-01-01")); // ✓

// Must be in the future
const deadline = date().future();
deadline.parse(new Date("2025-12-31")); // ✓

// Date range
const eventDate = date().between("2024-01-01", "2024-12-31");
eventDate.parse(new Date("2024-06-01")); // ✓

// Transform to ISO string
const isoDate = date().toISOString();
isoDate.parse(new Date()); // ✓ Returns ISO string

// Transform to timestamp
const timestamp = date().toTimestamp();
timestamp.parse(new Date()); // ✓ Returns number

Boolean Validation

import { boolean } from "validor";

// Basic boolean validation
const schema = boolean();
schema.parse(true); // ✓ true
schema.parse(false); // ✓ false

// Coercion mode
const coerced = boolean().coerce();
coerced.parse("true"); // ✓ true
coerced.parse("false"); // ✓ false
coerced.parse(1); // ✓ true
coerced.parse(0); // ✓ false

// Literal true
const mustBeTrue = boolean().true();
mustBeTrue.parse(true); // ✓ true
mustBeTrue.parse(false); // ✗ ValidationError

// Literal false
const mustBeFalse = boolean().false();
mustBeFalse.parse(false); // ✓ false
mustBeFalse.parse(true); // ✗ ValidationError

// Custom truthy values
const custom = boolean().coerce().addTruthy(["yes", "on"]);
custom.parse("yes"); // ✓ true

// Custom falsy values
const customFalsy = boolean().coerce().addFalsy(["no", "off"]);
customFalsy.parse("no"); // ✓ false

Object Validation

import { object, string, number } from "validor";

// Define object schema
const userSchema = object({
  name: string().min(2),
  email: string().email(),
  age: number().min(18),
});

// Validate
const user = userSchema.parse({
  name: "John",
  email: "[email protected]",
  age: 25,
});

// Strict mode (no extra keys allowed)
const strictSchema = object({
  id: number(),
  name: string(),
}).strict();

strictSchema.parse({
  id: 1,
  name: "John",
  extra: "not allowed", // ✗ ValidationError
});

// Optional fields
const optionalSchema = object({
  name: string(),
  nickname: string().optional(),
});

optionalSchema.parse({ name: "John" }); // ✓

// Nullable fields
const nullableSchema = object({
  name: string(),
  middleName: string().nullable(),
});

nullableSchema.parse({
  name: "John",
  middleName: null,
}); // ✓

// Nested objects
const addressSchema = object({
  street: string(),
  city: string(),
  country: string(),
});

const personSchema = object({
  name: string(),
  address: addressSchema,
});

personSchema.parse({
  name: "John",
  address: {
    street: "123 Main St",
    city: "New York",
    country: "USA",
  },
});

Array Validation

import { array, string, number } from "validor";

// Array of strings
const stringArray = array(string());
stringArray.parse(["a", "b", "c"]); // ✓

// Array of numbers
const numberArray = array(number());
numberArray.parse([1, 2, 3]); // ✓

// Array length constraints
const limitedArray = array(string()).min(1).max(10);
limitedArray.parse(["item"]); // ✓

// Non-empty arrays
const nonemptyArray = array(string()).nonempty();
nonemptyArray.parse([]); // ✗ ValidationError

// Array of objects
const users = array(
  object({
    name: string(),
    age: number(),
  })
);

users.parse([
  { name: "John", age: 25 },
  { name: "Jane", age: 30 },
]); // ✓

IPv4 Validation

import { ipv4 } from "validor";

// Basic IPv4 validation
const ip = ipv4();
ip.parse("192.168.1.1"); // ✓ '192.168.1.1'
ip.parse("256.1.1.1"); // ✗ ValidationError

// CIDR notation
const cidr = ipv4().cidr();
cidr.parse("192.168.1.0/24"); // ✓ '192.168.1.0/24'

// Require CIDR
const requireCidr = ipv4().cidrRequired();
requireCidr.parse("192.168.1.0/24"); // ✓
requireCidr.parse("192.168.1.1"); // ✗ ValidationError

// Private IP addresses
const privateIp = ipv4().private();
privateIp.parse("192.168.1.1"); // ✓
privateIp.parse("8.8.8.8"); // ✗ ValidationError

// Public IP addresses
const publicIp = ipv4().public();
publicIp.parse("8.8.8.8"); // ✓
publicIp.parse("192.168.1.1"); // ✗ ValidationError

// Loopback addresses
const loopback = ipv4().loopback();
loopback.parse("127.0.0.1"); // ✓

// Not loopback
const notLoopback = ipv4().notLoopback();
notLoopback.parse("192.168.1.1"); // ✓
notLoopback.parse("127.0.0.1"); // ✗ ValidationError

// IP range
const range = ipv4().range("192.168.1.1", "192.168.1.254");
range.parse("192.168.1.100"); // ✓

// Subnet validation
const subnet = ipv4().inSubnet("192.168.1.0/24");
subnet.parse("192.168.1.50"); // ✓

// Not in subnet
const notInSubnet = ipv4().notInSubnet("10.0.0.0/8");
notInSubnet.parse("192.168.1.1"); // ✓

Advanced Features

Default Values

import { string, number } from "validor";

// Static default
const name = string().default("Anonymous");
name.parse(undefined); // ✓ 'Anonymous'

// Function default
const timestamp = number().default(() => Date.now());
timestamp.parse(undefined); // ✓ Current timestamp

Custom Transformations

import { string, number } from "validor";

// Transform string
const upperCase = string().transform((s) => s.toUpperCase());
upperCase.parse("hello"); // ✓ 'HELLO'

// Transform number
const doubled = number().transform((n) => n * 2);
doubled.parse(5); // ✓ 10

// Chain transformations
const processed = string()
  .trim()
  .lowercase()
  .transform((s) => s.replace(/\s+/g, "-"));
processed.parse("  Hello World  "); // ✓ 'hello-world'

Async Validation

import { string } from "validor";

const schema = string().email();

// Async validation
const result = await schema.validate("[email protected]");
console.log(result); // ✓ '[email protected]'

Error Handling

import { object, string, ValidationError } from "validor";

const schema = object({
  name: string().min(3),
  email: string().email(),
});

try {
  schema.parse({
    name: "Jo",
    email: "invalid",
  });
} catch (error) {
  if (error instanceof ValidationError) {
    // Get all issues
    const issues = error.format();
    console.log(issues);
    // [
    //   { path: ['name'], message: '...', code: 'too_small' },
    //   { path: ['email'], message: '...', code: 'invalid_email' }
    // ]

    // Get formatted errors grouped by field
    const formatted = error.formatErrors();
    console.log(formatted);
    // {
    //   message: 'Validation failed',
    //   errors: {
    //     'name': ['The name must be at least 3 characters.'],
    //     'email': ['The email must be a valid email address.']
    //   }
    // }
  }
}

Safe Parsing

import { string } from "validor";

const schema = string().email();

// Safe parse (doesn't throw)
const result = schema.safeParse("invalid-email");

if (result.success) {
  console.log("Valid:", result.data);
} else {
  console.log("Invalid:", result.error);
}

Internationalization (i18n)

Validor supports multiple languages out of the box:

  • English (en) - Default
  • Uzbek (uz) - O'zbekcha
  • Russian (ru) - Русский
  • Karakalpak (kaa) - Qaraqalpaqsha

Setting Locale

import { setLocale } from "validor";

// Set locale globally
setLocale("kaa"); // Karakalpak
setLocale("en"); // English (default)
setLocale("uz"); // Uzbek
setLocale("ru"); // Russian

Custom Messages

import { i18n } from "validor";

// Add custom messages for a locale
i18n.setMessages("en", {
  required: "This field is required!",
  invalid_email: "Please enter a valid email address.",
});

// Configure with custom messages
i18n.configure("kaa", {
  uz: {
    required: "Bul maydan toltırılıwı shárt!",
    invalid_email: "Durıs email kiritiń",
  },
});

Per-Field Custom Messages

import { string } from "validor";

const email = string()
  .required("Email is required")
  .email("Please provide a valid email address");

email.parse(""); // ✗ 'Email is required'
email.parse("invalid"); // ✗ 'Please provide a valid email address'

TypeScript Support

Validor is built with TypeScript and provides full type inference:

import { object, string, number, array } from "validor";

const userSchema = object({
  id: number(),
  name: string(),
  email: string().email(),
  roles: array(string()),
  profile: object({
    bio: string().optional(),
    age: number().min(0),
  }),
});

// Type is automatically inferred
type User = typeof userSchema._type;
// {
//   id: number;
//   name: string;
//   email: string;
//   roles: string[];
//   profile: {
//     bio?: string;
//     age: number;
//   };
// }

// Parse with type safety
const user: User = userSchema.parse(data);

Integration Examples

Express.js Middleware

import express from "express";
import { object, string, number } from "validor";

const app = express();

const createUserSchema = object({
  name: string().min(2).max(50),
  email: string().email(),
  age: number().min(18),
});

app.post("/users", (req, res) => {
  try {
    const userData = createUserSchema.parse(req.body);
    // userData is validated and typed
    res.json({ success: true, user: userData });
  } catch (error) {
    res.status(400).json({
      success: false,
      error: error.message,
    });
  }
});

NestJS

import { Injectable, BadRequestException } from "@nestjs/common";
import { object, string } from "validor";

const createUserDto = object({
  name: string().min(2),
  email: string().email(),
});

@Injectable()
export class UserService {
  create(data: unknown) {
    try {
      const validated = createUserDto.parse(data);
      // Process validated data
      return validated;
    } catch (error) {
      throw new BadRequestException(error.message);
    }
  }
}

React Form Validation

import { useState } from "react";
import { object, string } from "validor";

const loginSchema = object({
  email: string().email(),
  password: string().min(8),
});

function LoginForm() {
  const [errors, setErrors] = useState({});

  const handleSubmit = (e) => {
    e.preventDefault();
    const formData = new FormData(e.target);
    const data = Object.fromEntries(formData);

    try {
      const validated = loginSchema.parse(data);
      // Submit validated data
      console.log("Valid:", validated);
      setErrors({});
    } catch (error) {
      setErrors(error.formatErrors().errors);
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <input name="email" type="email" />
      {errors.email && <span>{errors.email[0]}</span>}

      <input name="password" type="password" />
      {errors.password && <span>{errors.password[0]}</span>}

      <button type="submit">Login</button>
    </form>
  );
}

API Reference

Schema Methods

All schemas support these common methods:

  • .required(message?) - Make field required
  • .optional() - Make field optional
  • .nullable() - Allow null values
  • .default(value | fn) - Set default value
  • .transform(fn) - Transform the value
  • .parse(value) - Validate and return (throws on error)
  • .safeParse(value) - Validate and return result object
  • .validate(value) - Async validation

String Methods

  • .min(n, message?) - Minimum length
  • .max(n, message?) - Maximum length
  • .length(n, message?) - Exact length
  • .email(message?) - Email validation
  • .url(message?) - URL validation
  • .regex(pattern, message?) - Pattern matching
  • .nonempty(message?) - Non-empty string
  • .trim() - Trim whitespace
  • .lowercase() - Convert to lowercase
  • .uppercase() - Convert to uppercase

Number Methods

  • .min(n, message?) - Minimum value
  • .max(n, message?) - Maximum value
  • .positive(message?) - Must be > 0
  • .negative(message?) - Must be < 0
  • .nonnegative(message?) - Must be >= 0
  • .integer(message?) - Must be integer
  • .finite(message?) - Must be finite
  • .multipleOf(n, message?) - Must be multiple of n
  • .between(min, max, message?) - Range check

Date Methods

  • .min(date, message?) - Minimum date
  • .max(date, message?) - Maximum date
  • .past(message?) - Must be in past
  • .future(message?) - Must be in future
  • .between(start, end, message?) - Date range
  • .toISOString() - Transform to ISO string
  • .toTimestamp() - Transform to timestamp

Boolean Methods

  • .coerce() - Enable type coercion
  • .true(message?) - Must be true
  • .false(message?) - Must be false
  • .addTruthy(values) - Add custom truthy values
  • .addFalsy(values) - Add custom falsy values

Object Methods

  • .strict() - Disallow extra keys
  • .passthrough() - Allow extra keys

Array Methods

  • .min(n, message?) - Minimum array length
  • .max(n, message?) - Maximum array length
  • .length(n, message?) - Exact array length
  • .nonempty(message?) - Non-empty array

IPv4 Methods

  • .cidr() - Allow CIDR notation
  • .cidrRequired(message?) - Require CIDR notation
  • .private(message?) - Must be private IP
  • .public(message?) - Must be public IP
  • .loopback(message?) - Must be loopback
  • .notLoopback(message?) - Must not be loopback
  • .range(start, end, message?) - IP range check
  • .inSubnet(subnet, message?) - Must be in subnet
  • .notInSubnet(subnet, message?) - Must not be in subnet

License

MIT © Azizbek Berdimuratov

Credits

Built with ❤️ by the IDLE OSS team.