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 🙏

© 2025 – Pkg Stats / Ryan Hefner

jsonpolice

v13.0.0

Published

JSON Schema parser and validator

Downloads

2,569

Readme

jsonpolice

A powerful JavaScript library implementing JSON Schema draft 7 specification with support for newer versions. Validate JSON data against schemas with comprehensive validation rules, default value assignment, and property filtering capabilities.

npm version CI Coverage Status

Features

  • JSON Schema Draft 7, 2019-09 & 2020-12: Full implementation with automatic version detection
  • Schema Validation: Comprehensive validation with detailed error reporting
  • Default Values: Automatic assignment of default values for undefined properties
  • Property Filtering: Remove additional, readOnly, or writeOnly properties
  • Context-Aware: Support for read/write contexts to handle property visibility
  • External References: Resolve $ref references to external schemas
  • Modern Keywords: Support for dependentSchemas, dependentRequired, unevaluatedProperties, unevaluatedItems, and $defs
  • Backwards Compatible: Full compatibility with existing Draft 7 schemas
  • TypeScript Support: Full TypeScript definitions included
  • Modern ES Modules: Pure ESM package for modern JavaScript environments
  • Minimal Dependencies: Lightweight with minimal dependencies (jsonref and lodash)
  • Extensible: Support for custom validators via class extension

Installation

# npm
npm install jsonpolice

# pnpm
pnpm add jsonpolice

# yarn
yarn add jsonpolice

Requirements:

  • Node.js >= 18.17.0
  • ES Module support (this is a pure ESM package)

Quick Start

Basic Schema Validation

import { create } from 'jsonpolice';

const schema = await create({
  type: 'object',
  properties: {
    name: { type: 'string', minLength: 1 },
    email: { type: 'string', format: 'email' },
    age: { type: 'integer', minimum: 0, maximum: 120 }
  },
  required: ['name', 'email']
});

// Valid data
const validData = {
  name: 'John Doe',
  email: '[email protected]',
  age: 30
};

try {
  const result = await schema.validate(validData);
  console.log('Valid:', result);
} catch (error) {
  console.error('Validation failed:', error.message);
}

Schema with Default Values

import { create } from 'jsonpolice';

const schema = await create({
  type: 'object',
  properties: {
    name: { type: 'string' },
    role: { type: 'string', default: 'user' },
    active: { type: 'boolean', default: true },
    credits: { type: 'number', default: 100 }
  }
});

const data = { name: 'Alice' };
const result = await schema.validate(data, { setDefault: true });
console.log(result);
// Output: { name: 'Alice', role: 'user', active: true, credits: 100 }

Exports

jsonpolice exports the following members:

// Main function
import { create } from 'jsonpolice';

// Classes
import { Schema, StaticSchema } from 'jsonpolice';

// Types
import { SchemaOptions, ValidationOptions, SchemaVersion } from 'jsonpolice';

// Errors
import { ValidationError, SchemaError } from 'jsonpolice';

// Re-exports from jsonref
import { expand, parse, resolve } from 'jsonpolice';

// Default export
import create from 'jsonpolice'; // Same as named export

API Reference

create(schemaOrUri, options?)

Creates a new schema validator instance.

Parameters:

  • schemaOrUri (object | string): JSON Schema object or URI to fetch the schema
  • options (object, optional): Configuration options
    • version (string): Explicit JSON Schema version ('draft-07', '2019-09', '2020-12'). Auto-detected from $schema if not provided
    • scope (string): Base URI for resolving relative references (optional for inline schemas)
    • registry (object): Cache object to store resolved references for reuse
    • retriever (function): Function to fetch external references (url: string) => Promise<object>

Returns: Promise<Schema> - A schema validator instance

Example:

import { create } from 'jsonpolice';

// Create from schema object
const schema = await create({
  type: 'object',
  properties: {
    id: { type: 'string', format: 'uuid' }
  }
});

// Create from URI with custom retriever
const remoteSchema = await create('https://example.com/schema.json', {
  retriever: (url) => fetch(url).then(r => r.json())
});

schema.validate(data, options?)

Validates data against the schema.

Parameters:

  • data (any): The data to validate
  • options (object, optional): Validation options
    • setDefault (boolean): Add default values for undefined properties
    • removeAdditional (boolean): Remove properties not allowed by additionalProperties
    • context (string): Set to 'read' to remove writeOnly properties, 'write' to remove readOnly properties

Returns: The validated and potentially modified data

Throws: ValidationError if validation fails

Examples:

// Basic validation
const result = await schema.validate({ name: 'John' });

// Validation with default values
const withDefaults = await schema.validate(
  { name: 'John' }, 
  { setDefault: true }
);

// Remove additional properties
const cleaned = await schema.validate(
  { name: 'John', extra: 'removed' }, 
  { removeAdditional: true }
);

// Context-aware validation (API response context)
const forReading = await schema.validate(
  { password: 'secret', publicInfo: 'visible' }, 
  { context: 'read' }
);

Usage Examples

Complex Schema Validation

import { create } from 'jsonpolice';

const userSchema = await create({
  type: 'object',
  properties: {
    id: { type: 'string', format: 'uuid' },
    email: { type: 'string', format: 'email' },
    name: { type: 'string', minLength: 1, maxLength: 100 },
    age: { type: 'integer', minimum: 0, maximum: 150 },
    roles: {
      type: 'array',
      items: { type: 'string', enum: ['admin', 'user', 'guest'] },
      uniqueItems: true
    },
    profile: {
      type: 'object',
      properties: {
        bio: { type: 'string', maxLength: 500 },
        website: { type: 'string', format: 'uri' },
        socialMedia: {
          type: 'object',
          additionalProperties: { type: 'string', format: 'uri' }
        }
      }
    }
  },
  required: ['id', 'email', 'name'],
  additionalProperties: false
});

const userData = {
  id: '123e4567-e89b-12d3-a456-426614174000',
  email: '[email protected]',
  name: 'John Doe',
  age: 30,
  roles: ['user'],
  profile: {
    bio: 'Software developer',
    website: 'https://johndoe.dev',
    socialMedia: {
      twitter: 'https://twitter.com/johndoe',
      github: 'https://github.com/johndoe'
    }
  }
};

try {
  const validated = await userSchema.validate(userData);
  console.log('User data is valid:', validated);
} catch (error) {
  console.error('Validation failed:', error.message);
}

Working with External Schema References

import { create } from 'jsonpolice';

const schema = await create({
  type: 'object',
  properties: {
    user: { '$ref': 'https://json-schema.org/learn/examples/person.schema.json' },
    timestamp: { type: 'string', format: 'date-time' }
  }
}, {
  retriever: async (url) => {
    const response = await fetch(url);
    if (!response.ok) {
      throw new Error(`Failed to fetch schema: ${response.status}`);
    }
    return response.json();
  }
});

Context-Aware Validation (Read/Write Operations)

import { create } from 'jsonpolice';

const apiSchema = await create({
  type: 'object',
  properties: {
    id: { type: 'string', readOnly: true },
    email: { type: 'string', format: 'email' },
    password: { type: 'string', writeOnly: true, minLength: 8 },
    createdAt: { type: 'string', format: 'date-time', readOnly: true },
    updatedAt: { type: 'string', format: 'date-time', readOnly: true }
  },
  required: ['email']
});

// When creating a user (write context) - password is allowed, read-only fields are removed
const createData = {
  email: '[email protected]',
  password: 'secretpassword',
  createdAt: '2023-01-01T00:00:00Z' // This will be removed
};

const forCreation = await apiSchema.validate(createData, { context: 'write' });
console.log(forCreation); // { email: '[email protected]', password: 'secretpassword' }

// When returning user data (read context) - password is removed, read-only fields are kept
const responseData = {
  id: '123',
  email: '[email protected]',
  password: 'secretpassword', // This will be removed
  createdAt: '2023-01-01T00:00:00Z',
  updatedAt: '2023-01-01T00:00:00Z'
};

const forResponse = await apiSchema.validate(responseData, { context: 'read' });
console.log(forResponse); // { id: '123', email: '[email protected]', createdAt: '...', updatedAt: '...' }

Advanced Validation with Custom Formats

import { create } from 'jsonpolice';

const schema = await create({
  type: 'object',
  properties: {
    username: { 
      type: 'string', 
      pattern: '^[a-zA-Z0-9_]{3,20}$' 
    },
    birthDate: { 
      type: 'string', 
      format: 'date' 
    },
    phoneNumber: { 
      type: 'string', 
      pattern: '^\\+?[1-9]\\d{1,14}$' 
    },
    metadata: {
      type: 'object',
      patternProperties: {
        '^[a-z_]+$': { type: 'string' }
      },
      additionalProperties: false
    }
  }
});

const data = {
  username: 'john_doe_123',
  birthDate: '1990-05-15',
  phoneNumber: '+1234567890',
  metadata: {
    department: 'engineering',
    team_lead: 'jane_smith'
  }
};

const result = await schema.validate(data);

Conditional Validation with if/then/else

import { create } from 'jsonpolice';

const schema = await create({
  type: 'object',
  properties: {
    country: { type: 'string' },
    postalCode: { type: 'string' }
  },
  required: ['country'],
  if: {
    properties: { country: { const: 'US' } }
  },
  then: {
    properties: {
      postalCode: { pattern: '^\\d{5}(-\\d{4})?$' }
    }
  },
  else: {
    properties: {
      postalCode: { pattern: '^[A-Z0-9]{3,10}$' }
    }
  }
});

// Valid US postal code
await schema.validate({ country: 'US', postalCode: '12345' }); // ✓

// Valid non-US postal code
await schema.validate({ country: 'UK', postalCode: 'SW1A 1AA' }); // ✓

// Invalid - doesn't match US format
// await schema.validate({ country: 'US', postalCode: 'ABC' }); // ✗

Schema Definitions with $defs

import { create } from 'jsonpolice';

const schema = await create({
  $schema: 'https://json-schema.org/draft/2020-12/schema',
  type: 'object',
  properties: {
    billing_address: { $ref: '#/$defs/address' },
    shipping_address: { $ref: '#/$defs/address' }
  },
  $defs: {
    address: {
      type: 'object',
      properties: {
        street: { type: 'string' },
        city: { type: 'string' },
        state: { type: 'string', pattern: '^[A-Z]{2}$' },
        zipCode: { type: 'string', pattern: '^\\d{5}$' }
      },
      required: ['street', 'city', 'state', 'zipCode']
    }
  }
});

const orderData = {
  billing_address: {
    street: '123 Main St',
    city: 'Boston',
    state: 'MA',
    zipCode: '02101'
  },
  shipping_address: {
    street: '456 Oak Ave',
    city: 'Cambridge',
    state: 'MA',
    zipCode: '02139'
  }
};

const validated = await schema.validate(orderData);

Performance Optimization with Shared Registry

import { create } from 'jsonpolice';

const registry = {}; // Shared registry for caching

const userSchema = await create(userSchemaDefinition, { registry });
const productSchema = await create(productSchemaDefinition, { registry });
const orderSchema = await create(orderSchemaDefinition, { registry });

// All schemas will share the same registry, improving performance
// when they reference common schema definitions

Extensibility

jsonpolice supports custom validators through class extension. You can extend the StaticSchema class to add custom validation keywords:

import { StaticSchema, ValidationError, Schema } from 'jsonpolice';

class CustomSchema extends StaticSchema {
  // Override to register custom validator keywords
  addCustomValidators(validators, version) {
    validators.add('divisibleBy');
    return validators;
  }

  // Implement the custom validator method
  divisibleByValidator(data, spec, path, opts) {
    if (typeof data !== 'number') {
      return data;
    }
    if (data % spec.divisibleBy !== 0) {
      throw new ValidationError(
        path,
        Schema.scope(spec),
        'divisibleBy',
        `must be divisible by ${spec.divisibleBy}`
      );
    }
    return data;
  }

  // Override create to return the custom schema type
  static async create(dataOrUri, opts = {}) {
    const schema = new CustomSchema(dataOrUri, opts);
    await schema.spec();
    return schema;
  }
}

// Use the custom schema
const schema = await CustomSchema.create({
  type: 'number',
  divisibleBy: 3
});

await schema.validate(9);  // ✓ Valid
// await schema.validate(10); // ✗ Throws ValidationError

Key points for custom validators:

  • Extend StaticSchema (not Schema)
  • Override addCustomValidators(validators, version) to register keyword names
  • Implement validator methods with naming pattern: {keyword}Validator(data, spec, path, opts)
  • Override static create() to properly initialize your custom schema
  • Use await schema.spec() in the create method to initialize the schema

This pattern allows you to:

  • Add domain-specific validation rules
  • Implement custom format validators
  • Create specialized validators for your application needs
  • Maintain type safety and error handling consistency

Error Handling

jsonpolice provides detailed error information when validation fails:

import { create } from 'jsonpolice';

const schema = await create({
  type: 'object',
  properties: {
    email: { type: 'string', format: 'email' },
    age: { type: 'integer', minimum: 0 }
  },
  required: ['email']
});

try {
  await schema.validate({
    email: 'invalid-email',
    age: -5
  });
} catch (error) {
  console.log(error.name); // 'ValidationError'
  console.log(error.message); // Detailed error message
  console.log(error.errors); // Array of specific validation errors
  
  // Each error contains:
  // - path: JSON Pointer to the invalid property
  // - message: Human-readable error description
  // - constraint: The violated constraint
  // - value: The invalid value
}

Supported JSON Schema Keywords

jsonpolice implements the complete JSON Schema Draft 7 specification:

Type Validation

  • type - Validate basic types (string, number, integer, boolean, array, object, null)
  • enum - Validate against enumerated values
  • const - Validate against a constant value

String Validation

  • minLength, maxLength - String length constraints
  • pattern - Regular expression pattern matching
  • format - Built-in format validation including:
    • Date/Time: date, time, date-time
    • Email: email, idn-email
    • Hostnames: hostname, idn-hostname
    • IP Addresses: ipv4, ipv6
    • URIs: uri, uri-reference, iri, iri-reference, uri-template
    • JSON Pointers: json-pointer, relative-json-pointer
    • Other: uuid, regex, semver

Number Validation

  • minimum, maximum - Numeric range validation
  • exclusiveMinimum, exclusiveMaximum - Exclusive numeric ranges
  • multipleOf - Multiple validation

Array Validation

  • items - Validate array items against schema(s)
  • additionalItems - Handle additional items beyond defined schemas
  • minItems, maxItems - Array length constraints
  • uniqueItems - Ensure array items are unique
  • contains - At least one item must match schema

Object Validation

  • properties - Define property schemas
  • patternProperties - Properties matching regex patterns
  • additionalProperties - Handle additional properties
  • required - Required properties
  • minProperties, maxProperties - Object size constraints
  • dependencies - Property dependencies (Draft 7)
  • propertyNames - Validate property names

Schema Composition

  • allOf - Must match all schemas
  • anyOf - Must match at least one schema
  • oneOf - Must match exactly one schema
  • not - Must not match schema
  • if/then/else - Conditional validation

Meta-Schema Support

  • $ref - Reference resolution
  • $id - Schema identification
  • definitions - Schema definitions (Draft 7)
  • $defs - Schema definitions (2019-09+, preferred over definitions)

Metadata Keywords

  • title - Schema title for documentation
  • description - Schema description for documentation
  • default - Default values for properties
  • examples - Example values for documentation
  • readOnly - Properties that should not be sent in requests
  • writeOnly - Properties that should not be sent in responses
  • deprecated - Mark properties as deprecated (2019-09+)

Content Keywords

  • contentEncoding - Describe string content encoding (e.g., base64)
  • contentMediaType - Describe string content media type (e.g., application/json)

New Features in JSON Schema 2019-09 & 2020-12

  • dependentSchemas - Schema-based dependencies (replaces object-form dependencies)
  • dependentRequired - Property-based dependencies (replaces array-form dependencies)
  • unevaluatedProperties - Handle properties not evaluated by other keywords
  • unevaluatedItems - Handle array items not evaluated by other keywords
  • Automatic version detection from $schema property
  • Full backwards compatibility with Draft 7 schemas

JSON Schema Version Support

jsonpolice automatically detects the JSON Schema version from the $schema property:

// Draft 7 (default)
const draft7Schema = await create({
  $schema: 'http://json-schema.org/draft-07/schema#',
  type: 'string'
});

// JSON Schema 2019-09
const schema2019 = await create({
  $schema: 'https://json-schema.org/draft/2019-09/schema',
  type: 'object',
  dependentRequired: {
    name: ['surname']
  }
});

// JSON Schema 2020-12
const schema2020 = await create({
  $schema: 'https://json-schema.org/draft/2020-12/schema',
  type: 'object',
  properties: {
    name: { type: 'string' }
  },
  unevaluatedProperties: false
});

// Explicit version
const explicitSchema = await create({
  type: 'string'
}, { version: '2020-12' });

TypeScript Support

Full TypeScript definitions are included:

import { create, Schema, ValidationError } from 'jsonpolice';

interface User {
  id: string;
  email: string;
  name: string;
}

const schema: Schema = await create({
  type: 'object',
  properties: {
    id: { type: 'string' },
    email: { type: 'string', format: 'email' },
    name: { type: 'string' }
  },
  required: ['id', 'email', 'name']
});

try {
  const validated: User = await schema.validate(data);
} catch (error: ValidationError) {
  console.error('Validation failed:', error.message);
}

Performance Tips

  1. Reuse schema instances - Create schemas once and reuse them for multiple validations
  2. Use shared registries - Share registries between related schemas to cache external references
  3. Optimize external references - Implement efficient retriever functions with caching
  4. Consider validation options - Only use setDefault, removeAdditional, and context when needed
  5. Deep cloning optimization - The library automatically optimizes cloning for primitives vs objects in anyOf/oneOf validations

Compatibility

Node.js

jsonpolice requires Node.js >= 18.17.0 and is published as a pure ESM package.

Using with CommonJS:

If you need to use jsonpolice in a CommonJS project, you have several options:

  1. Dynamic import (recommended):
// CommonJS file
const createSchema = async () => {
  const { create } = await import('jsonpolice');
  const schema = await create({ type: 'string' });
  return schema;
};
  1. Convert your project to ESM by adding "type": "module" to your package.json

  2. Use a bundler like webpack or esbuild that can handle ESM dependencies

Browser Support

jsonpolice works in all modern browsers that support:

  • ES2022+ features
  • ES Modules (ESM)
  • Promise and async/await
  • JSON.parse/JSON.stringify

Supported environments:

  • Node.js >= 18.17.0
  • Chrome/Edge >= 91
  • Firefox >= 89
  • Safari >= 15
  • Modern bundlers (webpack, vite, rollup, esbuild)

License

MIT License - see the LICENSE file for details.

Contributing

Contributions are welcome! Please ensure all tests pass and maintain 100% code coverage:

# Install dependencies
pnpm install

# Run tests
pnpm test

# Check code coverage (must be 100%)
pnpm run cover
pnpm run check-coverage

# Build the project
pnpm run build

# Clean build artifacts
pnpm run clean

Development Guidelines:

  • Maintain 100% test coverage for all new code
  • Follow existing code style and conventions
  • Add tests for bug fixes and new features
  • Update documentation for API changes
  • Ensure all tests pass before submitting PRs

Resources