livr
v2.13.3
Published
Lightweight validator supporting Language Independent Validation Rules Specification (LIVR 2.0 and LIVR 2.5)
Maintainers
Readme
LIVR Validator
Validation rules that are just data. The fastest JavaScript validator in the benchmarks, also when rules are built at runtime, with TypeScript types inferred from the schema and zero dependencies.
Documentation
The full documentation is at js.livr-spec.org:
- Getting started: install, the first validator, errors and types
- Custom rules: aliases, rule functions and their types
- TypeScript: type inference and strict mode
- API:
ValidatorandAsyncValidator, their options and methods, registering rules - LIVR specification: the rules and the test suite shared by all implementations
The guides, the API and the TypeScript guide are also in the package, in node_modules/livr/docs. For AI assistants: llms.txt, and the whole documentation in one file, llms-full.txt.
Why LIVR
Rules are just data. A schema is a plain JSON structure, not code. Store it in a database, send it over the wire, share it with services written in other languages, build it at runtime, and load it safely even from untrusted sources: LIVR never turns a schema into code. Why schemas as data →
Incredible performance, also for rules built at runtime. The fastest in the benchmarks against Valibot, Zod, fastest-validator, Ajv and Joi. Most validators are fast only when a schema is compiled once; when rules come from a database, differ per tenant or come from a form builder, every call needs a new validator. There LIVR is tens to hundreds of times faster than Zod, fastest-validator and Ajv, and needs less than a microsecond to create a validator and validate a form. Benchmarks →
Type inference. TypeScript infers the type of the validated data from the schema itself: required and optional fields, literal unions from one_of, nested objects, lists and your own rules. No second definition to keep in sync. How it works →
Zero dependencies. Nothing to audit but LIVR itself. About 2 KB for the core, 5 KB with the 35 rules of LIVR 2.0 and 13 KB with all 68 built-in rules, min+gzip, and it runs in Node.js, browsers and edge runtimes. Why zero dependencies →
Proven. Maintained since 2013 and built on the LIVR specification (it implements LIVR 2.0 and LIVR 2.5), whose shared test suite collects edge cases found in implementations in 11 languages. More →
Also: error codes shaped like your data, validation and transformation in one pass, aliases and custom rules, and a sync and an async validator. The design decisions explain why LIVR works the way it does.
Installation
npm install livrWorks in Node.js and, through any bundler or an ESM CDN like https://esm.sh/livr, in browsers.
Quick Start
import LIVR from 'livr';
const validator = new LIVR.Validator({
name: 'required',
email: ['required', 'email'],
age: 'positive_integer',
password: ['required', { min_length: 8 }],
password2: { equal_to_field: 'password' },
});
const validData = validator.validate(userData);
if (validData) {
saveUser(validData); // validated data, fields without rules removed
} else {
console.log(validator.getErrors());
// { email: 'WRONG_EMAIL', password: 'TOO_SHORT' }
}With TypeScript, the type comes from the schema:
import LIVR from 'livr';
import type { InferFromSchema } from 'livr/types';
const schema = {
name: ['required', 'string'],
age: 'positive_integer',
role: { one_of: ['admin', 'user'] },
} as const;
type User = InferFromSchema<typeof schema>;
// { name: string; age?: number; role?: 'admin' | 'user' }
const validator = new LIVR.Validator<User>(schema);In a new project, turn on strict mode: a misspelled rule or a rule without a type is a compile error then, instead of the unknown type. It is off by default, so existing projects keep compiling: turn it on there once every custom rule has a type.
// livr-options.ts: the import makes the file a module and loads the types of livr
import type {} from 'livr/types/inference';
declare module 'livr/types/inference' {
interface TypeOptions {
strictRules: true;
}
}Next: usage guide (modifiers, async validation, smaller bundles), custom rules, API reference.
Custom Rules
An alias gives a set of existing rules a name and an error code. A rule function gets the rule arguments and returns a function that checks one value: it returns an error code, or nothing when the value is valid. With TypeScript, declare the type of every custom rule and alias in RuleTypes, the type of the value after the rule: registering a rule does not give it a type.
import LIVR from 'livr';
import type { InferFromSchema } from 'livr/types';
import type { As, AliasType } from 'livr/types/inference';
// An alias: 'phone' in a schema runs 'string' and 'like', with an error code of its own
const phoneRules = ['string', { like: '^\\+?[0-9]{10,15}$' }] as const;
LIVR.Validator.registerAliasedDefaultRule({
name: 'phone',
rules: phoneRules,
error: 'WRONG_PHONE',
});
// A rule function: { starts_with: 'SKU-' } calls starts_with('SKU-') once, when a validator
// compiles its rules, and the function it returns checks each value
LIVR.Validator.registerDefaultRules({
starts_with(prefix: string) {
return (value: unknown) => {
if (LIVR.util.isNoValue(value)) return undefined; // empty values are the job of 'required'
if (typeof value !== 'string' || !value.startsWith(prefix)) return 'WRONG_PREFIX';
return undefined;
};
},
});
// The types, in any .ts file of the project with an import or an export
declare module 'livr/types/inference' {
interface RuleTypes {
phone: AliasType<typeof phoneRules>;
starts_with: As<string>; // checks a string, `startsWith` gets the type too
}
}
const schema = {
phone: ['required', 'phone'],
sku: { starts_with: 'SKU-' },
} as const;
type Product = InferFromSchema<typeof schema>; // { phone: string; sku?: string }See the types in the TypeScript Playground. The function a rule returns gets (value, allValues, outputArr): push a value to outputArr to change the output. validator.registerRules() and validator.registerAliasedRule() register rules for one validator, and rules of AsyncValidator (livr/async) can return a promise.
More: the custom rules guide, types of rules with arguments, meta rules and rule packages in the TypeScript guide, and strict mode, which makes a misspelled rule or a rule without a type a compile error. In the package: docs/guide/custom-rules.md and docs/TYPESCRIPT.md.
Built-in Rules
livr implements LIVR 2.0 and LIVR 2.5: every rule of both versions is built in.
| Category | Rules |
|----------|-------|
| Common | required · not_empty · not_empty_list · any_object · required_if2.5 · any2.5 · boolean2.5 |
| String | string · eq · one_of · max_length · min_length · length_between · length_equal · like · not_one_of2.5 · digits2.5 · starts_with2.5 · ends_with2.5 · contains2.5 |
| Numeric | integer · positive_integer · decimal · positive_decimal · max_number · min_number · number_between · max_decimals2.5 |
| Special | email · url · iso_date ({ min, max }2.5) · iso_datetime2.5 · iso_time2.5 · equal_to_field |
| Meta | nested_object · variable_object · list_of · list_of_objects · list_of_different_objects · or · when2.5 · values2.5 · keys2.5 |
| List2.5 | list_length · unique |
| Format2.5 | uuid · ip · cidr · hostname · base64 · hex · digest · mongo_id · hex_color · mac · e164 · semver · jwt |
| Checksum2.5 | luhn · credit_card · iban |
| Modifiers | trim · to_lc · to_uc · remove · leave_only · default · split2.5 |
Rules and categories marked 2.5 were added in LIVR 2.5 and are loaded on first use. Implementations of LIVR 2.0 in other languages do not have them. The rules page shows the TypeScript type of every rule.
Need more rules? Check out livr-extra-rules for additional validators.
Contributing
Found a bug or have a feature request? Please open an issue on GitHub. The documentation site is built from docs/.
License
MIT License - see LICENSE for details.
Author
Viktor Turskyi (@koorchik)
Contributors
- eNdiD
