JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 104
  • Score
    100M100P100Q72274F
  • License MIT

The fastest JSON Schema validator for JavaScript and TypeScript with Type Inference. Supports Draft 06, 07, 2019-09, and 2020-12.

Package Exports

  • @jetio/validator

Readme

jet-validator

The Fastest JSON Schema Validator in JavaScript with Json Schema Compliant Type Inference

npm version License: MIT Bundle Size


๐Ÿš€ Why jet-validator?

TypeScript Inference | 5-44x faster compilation | 26KB gzipped | Zero dependencies | JSON Schema Draft 06-2020-12

jet-validator compiles JSON Schemas into highly optimized validation functions in sub-millisecond time. Unlike traditional validators that interpret schemas at runtime, jet-validator generates specialized code tailored to your exact schema structure.

Full TypeScript Type Inference that mirrors run time performance via @jetio/schema-builder

Built with simplicity in mind.

Just one import for all supported drafts, check full documentation for how to specify draft for $ref keyword for schema 07 and earlier.

Key Features

  • โšก Lightning Fast - 19x faster compilation than AJV (sub-millisecond compilation)
  • ๐Ÿ”ง Type inference including support for advanced keywords - allOf, oneOf, anyOf, If/Then/ElseIf/Else, unevaluated and additional items/properties, patternProperties etc.
  • โœ… Highly Compliant - 99.5%+ compliance on JSON Schema Test Suite across all supported drafts
  • ๐Ÿ“ฆ Smaller Bundle - 26KB Gzipped with built-in format validators and custom error messages(no external packages needed)
  • ๐Ÿ“ Built-in Formats - email, uri, date-time, uuid, and more included (no extra packages!)
  • โš ๏ธ Custom Error Messages - Flexible error customization with errorMessage keyword
  • ๐ŸŽฏ Zero Dependencies - Pure TypeScript implementation
  • ๐Ÿ’ช TypeScript-First - Full type safety out of the box
  • ๐Ÿ”ง Enhanced Features - elseIf conditionals, advanced $data references, custom keywords, custom formats
  • ๐Ÿ”„ Partial AJV Compatibility - Similar API, minimal code changes needed
  • ๐Ÿ“Š Multiple Error Modes - Fail-fast or collect all errors
  • ๐ŸŒ Async Schema Loading - Load schemas from HTTP, databases, or file systems
  • ๐Ÿšซ No Infinite Recursion - Smart resolution process prevents stack overflow errors

๐Ÿ› ๏ธ Schema Builder with TypeScript Inference

Want a fluent, type-safe API for building schemas with automatic typescript types? Check out @jetio/schema-builder:

import { SchemaBuilder, JetValidator } from "@jetio/schema-builder";

const schema = new SchemaBuilder()
  .object()
  .properties({
    name: s => s.string().minLength(2),
    email: s => s.string().format("email"),
    age: s => s.integer().minimum(18)
  })
  .required(["name", "email"])
  .build();

// Type Inference
type User = Jet.Infer<typeof schema>

const validator = new JetValidator();
const validate = validator.compile(schema);

The schema-builder package includes this validator, so you get both in one install.

What Makes JetValidator Different

1. Full TypeScript Type Inference

Types that mirror runtime validation exactly.

Build schemas with @jetio/schema-builder:

import { SchemaBuilder as s } from '@jetio/schema-builder';

const schema = s.object({
  type: s.enum(['admin', 'user']),
  permissions: s.string()
}).oneOf([
  s.object({ 
    type: s.const('admin'), 
    permissions: s.const('all') 
  }),
  s.object({ 
    type: s.const('user'), 
    permissions: s.const('read') 
  })
]);

// Automatically inferred as discriminated union:
type User = 
  | { type: 'admin'; permissions: 'all' }
  | { type: 'user'; permissions: 'read' };

Advanced features:

  • โœ… Discriminated unions from oneOf
  • โœ… Pattern properties as template literals
  • โœ… Conditional type narrowing
  • โœ… Deep object merging
  • โœ… Exclusive unions with conflict detection

โ†’ See @jetio/schema-builder


2. elseIf Conditionals

Clean conditional validation without deep nesting.

Standard JSON Schema:

// Deeply nested if/else chains
{
  if: { properties: { type: { const: 'A' } } },
  then: { properties: { value: { minimum: 100 } } },
  else: {
    if: { properties: { type: { const: 'B' } } },
    then: { properties: { value: { minimum: 50 } } },
    else: { /* ... */ }
  }
}

JetValidator:

// Clean and flat
{
  if: { properties: { type: { const: 'A' } } },
  then: { properties: { value: { minimum: 100 } } },
  elseIf: [
    {
      if: { properties: { type: { const: 'B' } } },
      then: { properties: { value: { minimum: 50 } } }
    }
  ],
  else: { properties: { value: { minimum: 0 } } }
}

With schema builder:

const schema = s.object({ type: s.string(), value: s.number() })
  .if(s => s.properties({ type: s.const('A') }))
  .then(s => s.properties({ value: s.minimum(100) }))
  .elseIf(s => s.properties({ type: s.const('B') }))
  .then(s => s.properties({ value: s.minimum(50) }))
  .else(s => s.properties({ value: s.minimum(0) }));

3. No Stack Overflow Errors

Three-phase resolution eliminates infinite recursion.

  1. Phase 1: Collect all references and identifiers
  2. Phase 2: Assign unique function names
  3. Phase 3: Resolve references to function calls

Result: Complex schemas with circular references never cause stack overflow.

// This works perfectly (causes issues in some validators)
const schema = {
  $id: 'https://example.com/tree',
  type: 'object',
  properties: {
    value: { type: 'number' },
    left: { $ref: '#' },   // Circular
    right: { $ref: '#' }   // Circular
  }
};

4. Advanced $data References

Compare values within your data at validation time.

const schema = {
  type: 'object',
  properties: {
    password: { type: 'string', minLength: 8 },
    confirmPassword: {
      type: 'string',
      const: { $data: '1/password' }  // Must match
    },
    minPrice: { type: 'number' },
    maxPrice: { type: 'number' },
    currentPrice: {
      type: 'number',
      minimum: { $data: '1/minPrice' },
      maximum: { $data: '1/maxPrice' }
    }
  }
};

Works with: const, enum, min/max, pattern, and more.


5. Three Custom Keyword Types

Different types for different use cases:

// Type 1: Code keywords (inline generation)
jetValidator.addKeyword({
  keyword: 'range',
  code: (value, schema, context) => `...`
});

// Type 2: Compile keywords (return function)
jetValidator.addKeyword({
  keyword: 'divisibleBy',
  compile: (value) => (data) => data % value === 0
});

// Type 3: Validate keywords (async capable)
jetValidator.addKeyword({
  keyword: 'uniqueEmail',
  async: true,
  validate: async (value, data) => !(await db.exists(data))
});

6. Macro System

Transform schemas at compile time.

jetValidator.addKeyword({
  keyword: 'username',
  macro: (value, schema) => ({
    type: 'string',
    minLength: 3,
    maxLength: 20,
    pattern: '^[a-zA-Z0-9_]+$'
  })
});

// Use it
const schema = {
  properties: {
    username: { username: true }  // Expands to full validation
  }
};

7. Reference Inlining Optimization

Automatically eliminates function call overhead.

When safe, references are inlined instead of generating function calls:

// Instead of function calls:
if (!validate_name(data.name)) return false;

// Generates inlined code:
if (typeof data.name !== 'string' || data.name.length < 2) return false;

Result: Up to 80% reduction in function calls.


8. Zero Dependencies

Everything included in 26kB Gzipped:

โœ… Format validators (email, uri, date, uuid, etc.) โœ… Custom error messages โœ… $data references โœ… Type coercion โœ… Custom keywords โœ… Macro system

What Fast Compilation Enables

Because compilation is fast (1-5ms for complex schemas), you can:

โœ… Compile schemas on-the-fly - No caching required if performance isn't critical
โœ… Hot-reload validation rules - Update schemas without restarting
โœ… Dynamic schema generation - Build schemas based on user input or config
โœ… Per-request validators - Create custom validators for each request context
โœ… Faster Startup - Faster cold starts when compiling large numbers of schemas in serverless environments


๐Ÿ“ฆ Installation

npm install @jetio/validator
pnpm add @jetio/validator
yarn add @jetio/validator

๐Ÿš€ Try It Live

Open in StackBlitz

๐Ÿš€ Quick Start

import { JetValidator } from "@jetio/validator";

const jetValidator = new JetValidator();

const schema = {
  type: "object",
  properties: {
    name: { type: "string", minLength: 2 },
    age: { type: "number", minimum: 0, maximum: 120 },
    email: { type: "string", format: "email" },
  },
  required: ["name", "age"],
};

const validate = jetValidator.compile(schema);

// Valid data
console.log(
  validate({
    name: "Alice",
    age: 25,
    email: "alice@example.com",
  })
);
// Output: true

// Invalid data
console.log(
  validate({
    name: "A",
    age: 150,
  })
);
// Output: false

console.log(validate.errors);
// Output:
// [{
//   dataPath: '/name',
//   schemaPath: '/properties/name',
//   keyword: 'minLength',
//   message: 'must NOT have fewer than 2 characters'
// },
// {
//   dataPath: '/age',
//   schemaPath: '/properties/age',
//   keyword: 'maximum',
//   message: 'must be <= 120'
// }]

โ†’ See Getting Started Guide


๐Ÿ“Š JSON Schema Compliance

jet-validator supports JSON Schema Draft 06 through 2020-12 with exceptional compliance rates across all drafts.

Compliance Results

All results are from the official JSON Schema Test Suite. Optional tests were skipped. You can view the test code in the test folder of our repository.

Draft 2020-12

jet-validator:

Total tests: 1261
Passed: 1251 (99.2%)
Failed: 10 (0.8%)

AJV (for comparison):

Total tests: 1261
Passed: 1208 (95.8%)
Failed: 53 (4.2%)
๐Ÿ“‹ View jet-validator failures (10 tests)

dynamicRef.json (4 failures):

  • Multiple dynamic paths edge cases
  • Dynamic scope leaving edge cases

properties.json (1 failure):

  • __proto__ property edge case

required.json (4 failures) - JavaScript object property names __proto__ property edge case

vocabulary.json (1 failure):

  • Custom metaschema with no validation vocabulary
๐Ÿ“‹ View AJV failures (53 tests)

dynamicRef.json (23 failures) - Dynamic reference resolution issues
ref.json (8 failures) - Stack overflow errors with relative URIs (RangeError: Maximum call stack size exceeded)
required.json (4 failures) - JavaScript object property names __proto__ property edge case unevaluatedItems.json (12 failures) - Nested items evaluation
unevaluatedProperties.json (4 failures) - Dynamic reference with unevaluated properties
properties.json (1 failure) - JavaScript object property names
vocabulary.json (1 failure) - Custom metaschema validation


Draft 2019-09

jet-validator:

Total tests: 1227
Passed: 1206 (98.3%)
Failed: 21 (1.7%)

AJV (for comparison):

Total tests: 1227
Passed: 1206 (98.3%)
Failed: 23 (1.7%)
๐Ÿ“‹ View jet-validator failures (23 tests)

recursiveRef.json (13 failures):

  • $recursiveRef and $recursiveAnchor are intentionally not supported
  • These keywords are confusing and serve little practical purpose
  • Users should upgrade to Draft 2020-12 and use $dynamicRef/$dynamicAnchor instead (much better design)

Other failures:

  • defs.json (1) - Metaschema validation edge case recursiveAnchor
  • properties.json (1) - __proto__ property edge case required.json (4 failures) - JavaScript object property names __proto__ property edge case
  • ref.json (1) - Recursive anchor interaction
  • unevaluatedItems.json (1) - Recursive reference evaluation
  • unevaluatedProperties.json (1) - Recursive reference evaluation
  • vocabulary.json (1) - Custom metaschema (not supported)
๐Ÿ“‹ View AJV failures (21 tests)

ref.json (8 failures) - Stack overflow errors (RangeError: Maximum call stack size exceeded)
required.json (4 failures) - JavaScript object property names
unevaluatedItems.json (3 failures) - Nested items and conditional evaluation
recursiveRef.json (2 failures) - Recursive reference resolution
unevaluatedProperties.json (2 failures) - Conditional evaluation
properties.json (1 failure) - JavaScript object property names
vocabulary.json (1 failure) - Custom metaschema validation


Draft 07

jet-validator:

Total tests: 913
Passed: 908 (99.5%)
Failed: 5 (0.5%)

AJV (for comparison):

Total tests: 913
Passed: 905 (99.1%)
Failed: 8 (0.9%)
๐Ÿ“‹ View jet-validator failures (5 test)

properties.json (5 failure):

  • __proto__ property edge case required.json (4 failures) - JavaScript object property names __proto__ property edge case
๐Ÿ“‹ View AJV failures (8 tests)

required.json (4 failures) - JavaScript object property names __proto__ property edge case ref.json (3 failures) - Reference resolution and sibling keywords
properties.json (1 failure) - JavaScript object property names


Draft 06

jet-validator:

Total tests: 829
Passed: 824 (99.4%)
Failed: 5 (0.6%)

AJV: Specific version does not support Draft 06

๐Ÿ“‹ View jet-validator failures (5 test)

properties.json (1 failure): required.json (4 failures) - JavaScript object property names __proto__ property edge case

  • __proto__ property edge case

๐Ÿšซ No Infinite Recursion

Unlike other validators that struggle with certain schema patterns, jet-validator never encounters infinite recursion problems.

Due to our advanced resolution process, jet-validator never has infinite recursion problems.
Unlike other validators which exceed maximum call stack size on certain schemas,
jet-validator's recursion only goes as deep as the data being validated.

THERE IS NO INFINITE RECURSION IN jet-validator.

While AJV encounters stack overflow errors (RangeError: Maximum call stack size exceeded) on schemas with complex relative URI references, jet-validator handles these schemas without issue. Our three-phase resolution process (Collection โ†’ Assignment โ†’ Resolution) eliminates circular reference problems at compile time.

โ†’ Learn more about our Resolution Process


๐Ÿ“ Unsupported Keywords

For transparency, here are the keywords jet-validator intentionally does not support:

Draft 2019-09:

  • $recursiveRef / $recursiveAnchor - Confusing design, replaced by better $dynamicRef/$dynamicAnchor in Draft 2020-12
  • $vocabulary - Custom vocabulary system (edge case feature)

Draft 2020-12:

  • $vocabulary - Custom vocabulary system (edge case feature)

Why not support these?

  • $recursiveRef/$recursiveAnchor serve little practical purpose and are confusing. The Draft 2020-12 $dynamicRef/$dynamicAnchor keywords are much better designed.
  • $vocabulary is an edge case feature that adds complexity without significant benefit for most users.

โšก Performance

Why Compilation Speed Matters

Fast compilation enables new patterns:

// For simple schemas compile on every request (no caching needed)
app.post('/validate', (req) => {
  const schema = generateSchema(req.user);
  const validate = jetValidator.compile(schema); // 1.4ms
  return validate(req.body);
});

Serverless cold starts:

  • 20 routes: 560ms โ†’ 28ms compilation time
  • 532ms faster cold start

Dynamic schemas:

  • Generate based on user config
  • Hot-reload without restart
  • No caching infrastructure needed (depending on schema complexity)

Benchmarks

Environment: Ubuntu 1.6GHz laptop under realistic load (browser, IDE, system services)

Why under load? Production servers are never idle. These results reflect real-world conditions.

Summary

๐Ÿš€ Compilation: 19x faster (1.47ms vs 28.29ms) โœ… Valid Data: 58% win rate (36/62) ๐Ÿ›ก๏ธ Invalid Data: 73% win rate (45/62) ๐Ÿ† Overall: 72% win rate (89/124)

๐Ÿ“Š Full Benchmark Report | ๐Ÿ“ˆ Detailed Results

Tested against AJV v8.17.1 using official benchmarks with 65 schemas, 1000 warmups, 10000 iterations, 30 runs per test


Why This Matters

Caching becomes optional instead of mandatory:

// โŒ Other validators: Must cache to avoid compilation overhead
const validateUser = ajv.compile(userSchema); // Cache this!

// โœ… jet-validator: Fast enough to compile on-demand
app.post("/validate", (req, res) => {
  const schema = req.body.schema;
  const validate = jetValidator.compile(schema); // < 2ms
  res.json(validate(req.body.data));
});

๐ŸŽฏ Core Features

Basic Validation

Simple validation with comprehensive error reporting:

const jetValidator = new JetValidator();

const schema = {
  type: "object",
  properties: {
    username: { type: "string", minLength: 3, maxLength: 20 },
    email: { type: "string", format: "email" },
    age: { type: "number", minimum: 18 },
  },
  required: ["username", "email"],
};

const validate = jetValidator.compile(schema);
const result = validate({ username: "jo", email: "invalid" });

console.log(result); // false
console.log(validate.errors); // Detailed error information

โ†’ See Basic Validation Examples


Schema Compilation & Management

Compile once, validate many times with blazing speed:

// Synchronous compilation
const validate = jetValidator.compile(schema);

// Async compilation (for remote schemas)
const validate = await jetValidator.compileAsync(schema);

// Add schemas to registry for reuse
jetValidator.addSchema(schema, "user-schema");
const validate = jetValidator.getSchema("user-schema");

โ†’ See Schema Management & Compilation


Configuration Options

Customize validation behavior to match your needs:

const jetValidator = new JetValidator({
  // Error handling
  allErrors: true, // Collect all errors, not just first
  errorMessage: true, // Enable custom error messages

  // Validation strictness
  strict: true, // Strict mode for schema validation
  strictNumbers: true, // Reject NaN and Infinity
  strictRequired: true, // Fail on undefined required properties

  // Data modification
  coerceTypes: true, // Auto-convert types (string โ†’ number)
  useDefaults: true, // Apply default values
  removeAdditional: true, // Remove extra properties

  // Format validation
  validateFormats: true, // Enable format validation

  // Performance
  async: true, // Enable async validation
});

โ†’ See All Configuration Options


Error Handling

Rich, detailed error information with customizable messages:

const jetValidator = new JetValidator({
  allErrors: true,
  errorMessage: true,
});

const schema = {
  type: "object",
  properties: {
    password: {
      type: "string",
      minLength: 8,
      errorMessage: "Password must be at least 8 characters long",
    },
    confirmPassword: {
      type: "string",
      const: { $data: "1/password" },
      errorMessage: "Passwords must match",
    },
  },
};

const validate = jetValidator.compile(schema);
const result = validate({
  password: "short",
  confirmPassword: "different",
});

console.log(validate.errors); // Custom error messages included

Error Object Structure:

{
  dataPath: string;        // Path to invalid data: "/properties/user"
  schemaPath: string;      // Path to schema location: "/properties/user"
  keyword: string;         // Keyword that failed: "minLength"
  fullSchemaPath: string;  // Complete path: "/properties/user/minLength"
  message: string;         // Error message
  params?: object;         // Additional context
}

โ†’ See Error Handling Guide


Advanced Error Handling

jet-validator provides production-grade error handling out of the box:

Error Utility Methods

Built-in utilities for working with errors:

// Pretty-print errors with hierarchy
jetValidator.logErrors(validate.errors);

// Group errors by field (perfect for forms)
const fieldErrors = jetValidator.getFieldErrors(validate.errors);
// { '/email': ['Invalid format', 'Too short'], '/age': ['Must be positive'] }

// Format as readable string
jetValidator.errorsText(validate.errors, { separator: "\n" });

Custom Error Messages

Schema-level or parent-level customization:

const schema = {
  type: "object",
  properties: {
    email: {
      type: "string",
      format: "email",
      minLength: 5,
    },
  },
  errorMessage: {
    properties: {
      email: {
        type: "Email must be text",
        format: "Invalid email format",
        minLength: "Email too short",
      },
    },
  },
};

โ†’ See Complete Error Handling Guide


Schema References & Composition

Build modular, reusable schemas with $ref, $dynamicRef, and schema composition:

const schema = {
  $id: "https://example.com/schemas/user.json",
  type: "object",
  properties: {
    profile: { $ref: "#/$defs/profile" },
    address: { $ref: "https://example.com/schemas/address.json" },
  },
  $defs: {
    profile: {
      type: "object",
      properties: {
        name: { type: "string" },
        bio: { type: "string" },
      },
    },
  },
};

// Load remote schemas automatically
const jetValidator = new JetValidator({
  loadSchema: async (uri) => {
    const response = await fetch(uri);
    return response.json();
  },
});

const validate = await jetValidator.compileAsync(schema);

โ†’ See Schema References & Composition


Meta-Schema System

Validate your schemas before using them (supports JSON Schema Draft 06-2020-12):

const jetValidator = new JetValidator({
  validateSchema: true,
  meta: true,
});

// Your schema is automatically validated against the meta-schema
const validate = jetValidator.compile(schema);
// Throws error if schema is invalid

// Or validate explicitly
const isValid = jetValidator.validateSchema(schema);

โ†’ See Meta-Schema System


๐Ÿ”ฅ Advanced Features

Format Validation

Built-in formats plus easy custom format registration:

// Built-in formats (no extra packages needed!)
const schema = {
  type: "object",
  properties: {
    email: { type: "string", format: "email" },
    url: { type: "string", format: "uri" },
    date: { type: "string", format: "date-time" },
    ipv4: { type: "string", format: "ipv4" },
  },
};

// Add custom formats
jetValidator.addFormat("phone", /^\+?[1-9]\d{1,14}$/);

// Or with validation function
jetValidator.addFormat("even-number", {
  type: "number",
  validate: (value) => value % 2 === 0,
});

// Async format validation
jetValidator.addFormat("unique-email", {
  type: "string",
  async: true,
  validate: async (email) => {
    return !(await database.emailExists(email));
  },
});

โ†’ See Format Validation Guide


$data References

Compare and validate against other values in your data:

const schema = {
  type: "object",
  properties: {
    startDate: { type: "string", format: "date" },
    endDate: {
      type: "string",
      format: "date",
      // endDate must be after startDate
      formatMinimum: { $data: "1/startDate" },
    },
    minPrice: { type: "number" },
    maxPrice: { type: "number" },
    currentPrice: {
      type: "number",
      minimum: { $data: "1/minPrice" },
      maximum: { $data: "1/maxPrice" },
    },
  },
};

const jetValidator = new JetValidator({ $data: true });
const validate = jetValidator.compile(schema);

โ†’ See $data References Guide


elseIf Conditionals

Enhanced conditional validation without deep nesting:

// Standard JSON Schema (deeply nested)
{
  if: { properties: { type: { const: 'A' } } },
  then: { /* ... */ },
  else: {
    if: { properties: { type: { const: 'B' } } },
    then: { /* ... */ },
    else: { /* ... */ }
  }
}

// jet-validator elseIf (clean and readable)
{
  if: { properties: { type: { const: 'A' } } },
  then: { /* ... */ },
  elseIf: [
    {
      if: { properties: { type: { const: 'B' } } },
      then: { /* ... */ }
    },
    {
      if: { properties: { type: { const: 'C' } } },
      then: { /* ... */ }
    }
  ],
  else: { /* default case */ }
}

โ†’ See elseIf Keyword Guide


Custom Keywords

Extend jet-validator with your own validation logic:

// Add custom keyword
jetValidator.addKeyword({
  keyword: "isEven",
  type: "number",
  validate: (schema, data) => {
    return data % 2 === 0;
  },
  error: {
    message: "Number must be even",
  },
});

const schema = {
  type: "number",
  isEven: true,
};

const validate = jetValidator.compile(schema);
console.log(validate(4)); // true
console.log(validate(5)); // false

โ†’ See Custom Keywords Guide


๐Ÿ› ๏ธ Utility Functions

jet-validator exports utility functions that can be used for schema manipulation and debugging:

import {
  getSchemaAtPath,
  getJSONType,
  deepEqual,
  canonicalStringify,
  len_of,
} from "@jetio/validator/utilities";

getSchemaAtPath(schema, path)

Retrieve a sub-schema at a specific JSON Pointer path.

const schema = {
  properties: {
    user: {
      type: "object",
      properties: {
        name: { type: "string", minLength: 2 },
      },
    },
  },
};

const subSchema = getSchemaAtPath(schema, "#/properties/user/properties/name");
// Returns: { type: 'string', minLength: 2 }

Use case: Debugging validation errors - retrieve the exact schema that caused an error.


getJSONType(value)

Get the JSON Schema type of any value (matches JSON Schema type semantics).

getJSONType(42); // 'integer'
getJSONType(3.14); // 'number'
getJSONType([1, 2, 3]); // 'array'
getJSONType(null); // 'null'
getJSONType({}); // 'object'

Use case: Type checking in custom keywords or validation logic.


deepEqual(a, b)

Deep equality comparison (used internally for const keyword).

deepEqual({ a: 1, b: [2, 3] }, { a: 1, b: [2, 3] }); // true
deepEqual({ a: 1 }, { a: 1, b: 2 }); // false

Use case: Comparing complex data structures in tests or custom validators.


canonicalStringify(obj)

Deterministic JSON stringification (sorted keys for consistent hashing).

const obj1 = { b: 2, a: 1 };
const obj2 = { a: 1, b: 2 };

canonicalStringify(obj1) === canonicalStringify(obj2); // true
JSON.stringify(obj1) === JSON.stringify(obj2); // false

Use case: Caching, deduplication, or comparing schemas.


len_of(str)

Get the true Unicode character count (handles surrogate pairs correctly).

len_of("hello"); // 5
len_of("๐Ÿ‘‹๐Ÿฝ"); // 2 (not 4)
len_of("cafรฉ"); // 4

Use case: Validating minLength/maxLength for strings with emoji or special Unicode characters.


๐Ÿ”„ Migration from AJV

jet-validator has a very similar API to AJV:

// Before (AJV)
import Ajv from "ajv";
const ajv = new Ajv();
const validate = ajv.compile(schema);

// After (jet-validator)
import { JetValidator } from "@jetio/validator";
const jetValidator = new JetValidator();
const validate = jetValidator.compile(schema);

Benefits of switching:

  • โšก 19x faster compilation
  • ๐Ÿ“ฆ Smaller bundle size
  • ๐ŸŽฏ Built-in formats and error messages (no separate packages)
  • โœจ Enhanced features (elseIf, better $data)

What's different:

  • Error object structure
  • Custom keywords use different API
  • Meta-schema setup with cli

๐ŸŽ“ When to Use jet-validator

Perfect For:

โœ… High-throughput APIs - Validate thousands of requests per second
โœ… Dynamic schemas - Generate and compile schemas on-the-fly
โœ… TypeScript projects โœ… Serverless functions - Fast cold starts with quick compilation
โœ… Real-time validation - Hot-reload schemas without restart
โœ… Complex validation logic - Advanced $data and conditional validation
โœ… Bundle size matters - <100KB with all features included

Consider Alternatives If:

โš ๏ธ You need 100% JSON Schema spec compliance (we're at 99.5%)
โš ๏ธ You're already heavily invested in AJV ecosystem with custom plugins (we offer the same custom keywords but contexts are simpler and different, although much more easy to use) โš ๏ธ You need streaming validation for extremely large documents


๐ŸŒŸ What Makes jet-validator Different?

1. Three-Phase Resolution Process

jet-validator eliminates infinite recursion through a unique resolution approach. Unlike other validators that resolve references during traversal (causing stack overflow), jet-validator:

  1. Collects all references and identifiers
  2. Assigns unique function names to each location when inlining is impossible
  3. Resolves references by replacing them with function calls

This architecture enables both lightning-fast compilation and bulletproof circular reference handling.

โ†’ Learn more about the resolution process


2. Sub-Millisecond Compilation

Traditional validators:

// Slow: 10-20ms per compilation
const validate = ajv.compile(schema); // Must cache!

jet-validator:

// Fast: 0.7ms per compilation
const validate = jetValidator.compile(schema); // Can recompile!

3. Enhanced Conditionals with elseIf

Avoid deeply nested if/else chains with clean, readable elseIf syntax.


4. Advanced $data Support

Reference and compare values within your data during validation.


5. Built-in Everything

No need for separate packages - formats, error messages, and advanced features are all included.


๐Ÿ’ก Common Use Cases

API Request Validation

app.post("/api/users", (req, res) => {
  const validate = jetValidator.compile(userSchema);
  const result = validate(req.body);

  if (!result) {
    return res.status(400).json({ errors: validate.errors });
  }

  // Process valid data
});

Form Validation with Type Coercion

const jetValidator = new JetValidator({ coerceTypes: true });

const formSchema = {
  type: "object",
  properties: {
    age: { type: "number", minimum: 18 },
    agree: { type: "boolean" },
  },
};

const validate = jetValidator.compile(formSchema);

// Automatically converts strings to correct types
const data = { age: "25", agree: "true" };
validate(data);
console.log(data); // { age: '25', agree: 'true' }
// Note: JetValidator does not modify original data objects

Config File Validation

const configSchema = {
  type: "object",
  properties: {
    port: { type: "number", default: 3000 },
    host: { type: "string", default: "localhost" },
    ssl: { type: "boolean", default: false },
  },
};

const jetValidator = new JetValidator({ useDefaults: true });
const validate = jetValidator.compile(configSchema);

const config = {};
validate(config);
console.log(config); // {}
// Note: JetValidator does not modify original data objects

Dynamic Schema Generation

function createValidatorForUser(userType) {
  const schema = {
    type: "object",
    properties: {
      name: { type: "string" },
      role: { const: userType },
    },
  };

  // Fast enough to compile per request
  return jetValidator.compile(schema);
}

const validateAdmin = createValidatorForUser("admin");
const validateUser = createValidatorForUser("user");

๐Ÿ“š Documentation

Quick Navigation

Getting Started:

Core Concepts:

Advanced Features:

Complete Documentation:


Acknowledgments

The idea of this validator and its feature sets where heavily inspired by Ajv, from compilation approach to custom keywords and $data, even some formats though approch is entirely different.

Special thanks to the JSON Schema community for maintaining excellent specifications and comprehensive test suites.

๐Ÿค Contributing

We welcome contributions! jet-validator is a community project and we appreciate all help.

Ways to Contribute

  • ๐Ÿ› Report bugs - Found an issue? Open a bug report
  • ๐Ÿ’ก Suggest features - Have an idea? Open a feature request
  • ๐Ÿ“– Improve docs - Fix typos, add examples, clarify explanations
  • ๐Ÿงช Add test cases - Contribute to JSON Schema compliance
  • โšก Performance improvements - Make it even faster
  • ๐ŸŽจ Code contributions - Fix bugs or implement features

Development Setup

# Clone the repo
git clone https://github.com/official-jetio/validator
cd validator

# Install dependencies
npm install

# Build the project
cd JSON-Schema-Test-Suite/remotes
python3 -m http.server 1234 # Start local server for test suite
cd ..
cd ..

# Run JSON Schema Test Suite
npm run test:draft2020-12
npm run test:draft2019-09
npm run test:draft7
npm run test:draft6

# Run all tests
npm run test

# Run benchmarks
npm run build
node --expose-gc --max-old-space-size=4096 --max-semi-space-size=64 benchmarks/jet-validator-benchmark.js

Testing Philosophy

jet-validator uses the official JSON Schema Test Suite as its primary test suite. This ensures real-world compliance rather than contrived unit tests.

To test your changes:

  1. Make your changes to the codebase
  2. Build: npm run build
  3. Run the official test suite: npm run test:draft2020-12 (or appropriate draft)
  4. Check compliance rate - aim to maintain or improve current 99.5%+ compliance

The test runner (tests/test.ts) validates against:

  • 1,261+ tests for Draft 2020-12
  • 1,227+ tests for Draft 2019-09
  • 913+ tests for Draft 07
  • 829+ tests for Draft 06

Code Contributions

Before submitting a PR:

  1. โœ… Run the test suite and ensure no regressions
  2. โœ… Run npm run build successfully
  3. โœ… Update documentation if you're adding features
  4. โœ… Add benchmark tests if you're optimizing performance
  5. โœ… Explain the "why" in your PR description

Areas that need help:

  • Further optimization of common validation patterns
  • Additional format validators
  • Better error messages
  • Documentation improvements
  • Edge case handling from the test suite

Understanding the Codebase

The resolver (src/resolver.ts) is the heart of jet-validator. It handles:

  • Schema resolution and reference tracking
  • $ref, $dynamicRef, $anchor resolution
  • External schema loading
  • Circular reference prevention
  • Three-phase resolution process

If you're working on the resolver, please be extra careful - it's complex same with the compileSchema.ts but battle-tested against 3,000+ official test cases. They might look complex but once you are familiar with the code, everything looks simple. The compiler doesn't use any special ast approach or module just simple string concatenation.

Questions?


All contributions are appreciated! Even small improvements help make jet-validator better for everyone.


๐Ÿ› Found an Issue?

We track bugs and feature requests using GitHub Issues.

Before Filing an Issue

  1. Search existing issues - Your issue might already be reported
  2. Check the documentation - The answer might be in the 20k+ line docs
  3. Try the latest version - npm install @jetio/validator@latest

Types of Issues

  • ๐Ÿ› Bug Report: Something isn't working correctly

  • ๐Ÿ’ก Feature Request: Suggest a new feature or improvement

  • โ“ Question: Need help or clarification

  • ๐Ÿ“– Documentation: Docs are unclear or missing information

Good Bug Report Template

**Description:** Brief description of the issue

**Schema:**
\`\`\`json
{
"type": "object",
// your schema here
}
\`\`\`

**Data:**
\`\`\`json
{
// your data here
}
\`\`\`

**Expected:** What should happen

**Actual:** What actually happens

**Environment:**

- jet-validator version:
- Node.js version:
- OS:

๐Ÿ”— Quick Links:


โš ๏ธ Benchmark Methodology & Hardware Notes

Performance on Better Hardware:

Results will vary significantly on better hardware. While absolute numbers will improve across the board, jet-validator is expected to maintain its performance advantages, especially in compilation speed.


๐Ÿ† Conclusion

jet-validator has proven itself as a strong contender for JSON Schema validation in the Node.js ecosystem, offering:

โœ… 99.5% JSON Schema compliance - More spec-compliant
๐Ÿš€ Compilation: 19x faster (1.47ms vs 28.29ms) on average - Game-changer for serverless โœ… Valid Data: 58% win rate (36/62) ๐Ÿ›ก๏ธ Invalid Data: 73% win rate (45/62) ๐Ÿ† Overall: 72% win rate (89/124)

jet-validator is the clear choice for:

  • Serverless applications (cold start critical)
  • High-volume validation workloads
  • Real-world schema validation
  • Applications requiring fast compilation
  • Security-focused applications (invalid data detection)
  • Heavy reliance on complex $ref graphs
  • Schemas with complex conditional logic
  • Extremely deep composition chains (>10 levels)

Whether jet-validator is the "new king" depends on your use case, but the numbers speak for themselves.


๐Ÿ“„ License

MIT ยฉ Great Venerable



Built with โค๏ธ by The Venerable Supreme