A powerful JavaScript library implementing JSON Schema draft 7 specification. Validate JSON data against schemas with comprehensive validation rules, default value assignment, and property filtering capabilities.
- âś… JSON Schema Draft 7: Full implementation of JSON Schema specification
- âś… 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 - âś… TypeScript Support: Full TypeScript definitions included
- âś… Modern ES Modules: Supports both ESM and CommonJS
- âś… Zero Dependencies: Lightweight with minimal dependencies (only jsonref)
# npm
npm install jsonpolice
# pnpm
pnpm add jsonpolice
# yarn
yarn add jsonpolice
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);
}
import { create } from 'jsonpolice';
const schema = await create({
type: 'object',
properties: {
name: { type: 'string' },
role: { type: 'string', default: 'user' },
preferences: {
type: 'object',
properties: {
theme: { type: 'string', default: 'light' },
notifications: { type: 'boolean', default: true }
}
}
}
});
const data = { name: 'Alice' };
const result = await schema.validate(data, { setDefault: true });
console.log(result);
// Output: { name: 'Alice', role: 'user', preferences: { theme: 'light', notifications: true } }
Creates a new schema validator instance.
Parameters:
schemaOrUri
(object | string): JSON Schema object or URI to fetch the schemaoptions
(object, optional): Configuration optionsscope
(string): Base URI for resolving relative referencesregistry
(object): Cache object to store resolved references for reuseretriever
(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())
});
Validates data against the schema.
Parameters:
data
(any): The data to validateoptions
(object, optional): Validation optionssetDefault
(boolean): Add default values for undefined propertiesremoveAdditional
(boolean): Remove properties not allowed byadditionalProperties
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' }
);
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);
}
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();
}
});
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: 'user@example.com', 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: 'user@example.com', createdAt: '...', updatedAt: '...' }
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);
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
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
}
jsonpolice implements the complete JSON Schema Draft 7 specification:
type
- Validate basic types (string, number, integer, boolean, array, object, null)enum
- Validate against enumerated valuesconst
- Validate against a constant value
minLength
,maxLength
- String length constraintspattern
- Regular expression pattern matchingformat
- Built-in format validation (email, date-time, uri, uuid, etc.)
minimum
,maximum
- Numeric range validationexclusiveMinimum
,exclusiveMaximum
- Exclusive numeric rangesmultipleOf
- Multiple validation
items
- Validate array items against schema(s)additionalItems
- Handle additional items beyond defined schemasminItems
,maxItems
- Array length constraintsuniqueItems
- Ensure array items are uniquecontains
- At least one item must match schema
properties
- Define property schemaspatternProperties
- Properties matching regex patternsadditionalProperties
- Handle additional propertiesrequired
- Required propertiesminProperties
,maxProperties
- Object size constraintsdependencies
- Property dependenciespropertyNames
- Validate property names
allOf
- Must match all schemasanyOf
- Must match at least one schemaoneOf
- Must match exactly one schemanot
- Must not match schemaif
/then
/else
- Conditional validation
$ref
- Reference resolution$id
- Schema identificationdefinitions
- Schema definitions
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);
}
- Reuse schema instances - Create schemas once and reuse them for multiple validations
- Use shared registries - Share registries between related schemas to cache external references
- Optimize external references - Implement efficient retriever functions with caching
- Consider validation options - Only use
setDefault
,removeAdditional
, andcontext
when needed
jsonpolice works in all modern browsers and Node.js environments. It requires:
- ES2015+ support
- Promise support
- JSON.parse/JSON.stringify
MIT License - see the LICENSE file for details.
Contributions are welcome! Please ensure all tests pass:
pnpm install
pnpm test
pnpm run coverage