openapi-police
v5.0.1
Published
OpenAPI v3 validators and utilities
Readme
openapi-police
A powerful JavaScript library providing OpenAPI v3 validators and utilities for comprehensive API validation and compliance checking. Built on top of jsonpolice, it extends JSON Schema validation with OpenAPI-specific features like parameter style parsing and discriminator validation.
Features
- ✅ OpenAPI v3 Compliance: Full support for OpenAPI 3.0+ and 3.1 specifications
- ✅ Parameter Validation: Handle path, query, header, and cookie parameters with style parsing
- ✅ Schema Extensions: OpenAPI-specific schema enhancements (discriminator, nullable, etc.)
- ✅ Style Parsing: Support for parameter serialization styles (matrix, label, form, simple, etc.)
- ✅ Format Validation: Extended format validation for OpenAPI types
- ✅ TypeScript Support: Full TypeScript definitions included
- ✅ Modern ES Modules: Supports both ESM and CommonJS
- ✅ Built on jsonpolice: Leverages proven JSON Schema validation foundation with JSON Schema 2020-12 support
Installation
# npm
npm install openapi-police
# pnpm
pnpm add openapi-police
# yarn
yarn add openapi-policeQuick Start
Basic Schema Validation
import { SchemaObject } from 'openapi-police';
const schema = new SchemaObject({
type: 'object',
properties: {
id: { type: 'string', format: 'uuid' },
name: { type: 'string', nullable: true },
tags: {
type: 'array',
items: { type: 'string' },
uniqueItems: true
}
},
required: ['id']
});
try {
const data = {
id: '123e4567-e89b-12d3-a456-426614174000',
name: null, // nullable is allowed
tags: ['api', 'validation']
};
const validated = await schema.validate(data);
console.log('Valid data:', validated);
} catch (error) {
console.error('Validation failed:', error.message);
}Parameter Validation with Style Parsing
import { ParameterObject } from 'openapi-police';
// Query parameter with simple style (comma-separated)
const queryParam = new ParameterObject({
name: 'tags',
in: 'query',
required: true,
schema: {
type: 'array',
items: { type: 'string' }
},
style: 'simple',
explode: false
});
// Validate automatically parses the parameter style: ?tags=api,validation
const validated = await queryParam.validate('api,validation');
console.log(validated); // ['api', 'validation']
// Path parameter with simple style
const pathParam = new ParameterObject({
name: 'userId',
in: 'path',
required: true,
schema: { type: 'string', format: 'uuid' }
});
// Direct validation - parsing happens internally
await pathParam.validate('123e4567-e89b-12d3-a456-426614174000');API Reference
SchemaObject / StaticSchemaObject
Extends standard JSON Schema with OpenAPI-specific features.
Constructors:
// For simple schemas without $ref
import { StaticSchemaObject } from 'openapi-police';
new StaticSchemaObject(schema)
// For schemas with $ref - requires jsonref
import { StaticSchemaObject } from 'openapi-police';
import * as refs from 'jsonref';
new StaticSchemaObject(await refs.parse(schema, options))Parameters:
schema(object): OpenAPI Schema Object
Validation Options:
coerceTypes(boolean): Convert string values to the target type (default: false for SchemaObject, true for ParameterObject)setDefault(boolean): Apply default values from schemaremoveAdditional(boolean): Remove properties not in schemacontext('read' | 'write'): Validation context for readOnly/writeOnly
Features:
- nullable: Allow null values in addition to specified type
- discriminator: Polymorphism support with discriminator mapping
- format: Extended format validation for OpenAPI types
- readOnly/writeOnly: Context-aware validation
Example:
import { StaticSchemaObject } from 'openapi-police';
const schema = new StaticSchemaObject({
type: 'string',
nullable: true,
format: 'email'
});
await schema.validate(null); // Valid (nullable)
await schema.validate('[email protected]'); // Valid (email format)
await schema.validate('invalid-email'); // Throws ValidationErrorParameterObject
Handles OpenAPI parameter validation with automatic style parsing.
Constructor:
new ParameterObject(parameter)Parameters:
parameter(object): OpenAPI Parameter Object
Key Methods:
validate(data, options?, path?): Validates data and automatically parses parameter style- Automatically enables
coerceTypesby default - Automatically parses style-encoded strings based on parameter schema type
- Returns the validated and parsed data
- Automatically enables
Important Note:
⚠️ Do not call parseStyle() directly - it's an internal method. Use validate() which handles parsing automatically.
Supported Locations:
path- Path parameters (e.g.,/users/{id})query- Query string parameters (e.g.,?name=value)header- HTTP header parameterscookie- Cookie parameters
Style Support:
| Location | Supported Styles | Default | |----------|------------------|---------| | path | matrix, label, simple | simple | | query | simple, spaceDelimited, pipeDelimited, deepObject | simple | | header | simple, form, spaceDelimited, pipeDelimited | simple | | cookie | form, simple, spaceDelimited, pipeDelimited | form |
Note: The implementation differs from OpenAPI spec which specifies form as default for query parameters. This library uses simple for query arrays and deepObject for query objects.
Example:
import { ParameterObject } from 'openapi-police';
const param = new ParameterObject({
name: 'filter',
in: 'query',
schema: {
type: 'object',
properties: {
status: { type: 'string' },
priority: { type: 'string' }
}
},
style: 'deepObject',
explode: true
});
// Validate: ?filter[status]=active&filter[priority]=high
const parsed = await param.validate('filter[status]=active&filter[priority]=high');
console.log(parsed); // { status: 'active', priority: 'high' }MediaTypeObject
Handles validation for OpenAPI MediaType objects with content-type aware validation.
Constructor:
new MediaTypeObject(mediaType, contentType)Parameters:
mediaType(object): OpenAPI MediaType ObjectcontentType(string): The content type (e.g., 'application/json')
Example:
import { MediaTypeObject } from 'openapi-police';
const mediaType = new MediaTypeObject({
schema: {
type: 'object',
properties: {
message: { type: 'string' }
}
}
}, 'application/json');
await mediaType.validate({ message: 'Hello' });Usage Examples
Complex Schema with Discriminator
import { SchemaObject } from 'openapi-police';
const petSchema = new SchemaObject({
discriminator: {
propertyName: 'petType',
mapping: {
cat: '#/components/schemas/Cat',
dog: '#/components/schemas/Dog'
}
},
oneOf: [
{ $ref: '#/components/schemas/Cat' },
{ $ref: '#/components/schemas/Dog' }
]
});
// The discriminator will automatically select the correct schema
// based on the petType property value
const catData = {
petType: 'cat',
name: 'Fluffy',
huntingSkill: 'excellent'
};
const validated = await petSchema.validate(catData);Advanced Parameter Styles
import { ParameterObject } from 'openapi-police';
// Matrix style for path parameters
const matrixParam = new ParameterObject({
name: 'coordinates',
in: 'path',
required: true,
schema: {
type: 'object',
properties: {
lat: { type: 'number' },
lng: { type: 'number' }
}
},
style: 'matrix',
explode: true
});
// Validate with type coercion (strings → numbers)
// Input: ;lat=50.1;lng=8.7
const coords = await matrixParam.validate(';lat=50.1;lng=8.7');
console.log(coords); // { lat: 50.1, lng: 8.7 }
// Note: ParameterObject automatically enables coerceTypes
// Label style for path parameters
const labelParam = new ParameterObject({
name: 'tags',
in: 'path',
required: true,
schema: {
type: 'array',
items: { type: 'string' }
},
style: 'label',
explode: false
});
// Validate with label style: .red.green.blue
const tags = await labelParam.validate('.red.green.blue');
console.log(tags); // ['red', 'green', 'blue']Working with Headers and Cookies
import { ParameterObject } from 'openapi-police';
// Header parameter
const headerParam = new ParameterObject({
name: 'X-API-Version',
in: 'header',
required: true,
schema: {
type: 'string',
pattern: '^v\\d+$'
}
});
await headerParam.validate('v1'); // Valid
await headerParam.validate('invalid'); // Throws ValidationError
// Cookie parameter with form style (default)
const cookieParam = new ParameterObject({
name: 'session',
in: 'cookie',
schema: {
type: 'object',
properties: {
userId: { type: 'string' },
token: { type: 'string' }
}
},
style: 'form',
explode: false
});
// Validate cookie value: "session=userId,123,token,abc123"
const session = await cookieParam.validate('session=userId,123,token,abc123');
console.log(session); // { userId: '123', token: 'abc123' }Type Validation with nullable
import { SchemaObject } from 'openapi-police';
const schema = new SchemaObject({
type: 'integer',
nullable: true,
minimum: 0,
maximum: 100
});
await schema.validate(null); // Valid (nullable)
await schema.validate(50); // Valid (integer in range)
await schema.validate(150); // Throws ValidationError (exceeds maximum)
await schema.validate('50'); // Throws ValidationError (wrong type)Error Handling
openapi-police provides detailed validation errors:
import { SchemaObject, ParameterObject } from 'openapi-police';
try {
const schema = new SchemaObject({
type: 'object',
properties: {
email: { type: 'string', format: 'email' }
},
required: ['email']
});
await schema.validate({ email: 'invalid-email' });
} catch (error) {
console.log(error.name); // 'ValidationError'
console.log(error.message); // Detailed error description
console.log(error.path); // JSON Pointer to invalid property
}TypeScript Support
Full TypeScript definitions are included:
import { SchemaObject, ParameterObject } from 'openapi-police';
interface APIResponse {
id: string;
data: any;
nullable?: string | null;
}
const responseSchema = new SchemaObject({
type: 'object',
properties: {
id: { type: 'string', format: 'uuid' },
data: {},
nullable: { type: 'string', nullable: true }
},
required: ['id', 'data']
});
const validated: APIResponse = await responseSchema.validate(responseData);Integration with OpenAPI Specifications
openapi-police is designed to work seamlessly with OpenAPI specifications:
import { SchemaObject, ParameterObject } from 'openapi-police';
// From OpenAPI spec
const openApiSpec = {
paths: {
'/users/{userId}': {
get: {
parameters: [
{
name: 'userId',
in: 'path',
required: true,
schema: { type: 'string', format: 'uuid' }
},
{
name: 'include',
in: 'query',
schema: {
type: 'array',
items: { type: 'string' }
},
style: 'simple',
explode: false
}
]
}
}
}
};
// Create validators from spec
const pathParam = new ParameterObject(openApiSpec.paths['/users/{userId}'].get.parameters[0]);
const queryParam = new ParameterObject(openApiSpec.paths['/users/{userId}'].get.parameters[1]);
// Use in request validation
await pathParam.validate('123e4567-e89b-12d3-a456-426614174000');
await queryParam.validate('profile,settings,preferences'); // simple style: comma-separatedOpenAPI 3.1 Features
openapi-police now supports OpenAPI 3.1 specification features:
JSON Schema 2020-12 Support
OpenAPI 3.1 aligns with JSON Schema Draft 2020-12, providing enhanced validation capabilities:
import { SchemaObject } from 'openapi-police';
const schema = new SchemaObject({
type: 'object',
properties: {
// OpenAPI 3.1 can specify the JSON Schema dialect
name: { type: 'string' },
tags: {
type: 'array',
items: { type: 'string' },
prefixItems: [{ const: 'primary' }] // JSON Schema 2020-12 feature
}
}
});Webhooks Support
OpenAPI 3.1 introduces webhooks for describing incoming HTTP requests:
const openApiDoc = {
openapi: '3.1.0',
info: { title: 'Webhook API', version: '1.0.0' },
paths: {},
webhooks: {
'newPet': {
post: {
requestBody: {
content: {
'application/json': {
schema: {
type: 'object',
properties: {
id: { type: 'integer' },
name: { type: 'string' }
}
}
}
}
},
responses: {
'200': { description: 'Webhook processed' }
}
}
}
}
};Deep Object Parameter Style
Now supports the deepObject style for complex query parameters:
import { ParameterObject } from 'openapi-police';
const deepParam = new ParameterObject({
name: 'filter',
in: 'query',
style: 'deepObject',
explode: true,
schema: {
type: 'object',
properties: {
status: { type: 'string' },
priority: { type: 'string' },
category: { type: 'string' }
}
}
});
// Validate: ?filter[status]=active&filter[priority]=high&filter[category]=api
const parsed = await deepParam.validate('filter[status]=active&filter[priority]=high&filter[category]=api');
console.log(parsed); // { status: 'active', priority: 'high', category: 'api' }
// Note: deepObject style does not support array values - each key can only have one value
// If the same key appears multiple times, the last value overwrites previous onesParameter Content Validation
Enhanced support for parameter content validation:
import { ParameterObject } from 'openapi-police';
const contentParam = new ParameterObject({
name: 'data',
in: 'query',
content: {
'application/json': {
schema: {
type: 'object',
properties: {
filters: {
type: 'array',
items: { type: 'string' }
},
options: {
type: 'object',
properties: {
limit: { type: 'integer', minimum: 1 },
offset: { type: 'integer', minimum: 0 }
}
}
}
}
},
'application/xml': {
schema: { type: 'string' }
}
}
});
// Validate with content type
const jsonData = {
filters: ['active', 'verified'],
options: { limit: 10, offset: 0 }
};
await contentParam.validate(jsonData, { contentType: 'application/json' });JSON Schema Dialect
OpenAPI 3.1 documents can specify their JSON Schema dialect:
const openApi31Doc = {
openapi: '3.1.0',
info: { title: 'Modern API', version: '2.0.0' },
jsonSchemaDialect: 'https://json-schema.org/draft/2020-12/schema',
paths: {
'/items': {
get: {
parameters: [{
name: 'search',
in: 'query',
schema: {
type: 'object',
patternProperties: {
'^[a-zA-Z]+$': { type: 'string' }
}
}
}]
}
}
}
};Validation Options
All validate methods accept an optional options parameter with the following properties:
Common Options:
coerceTypes(boolean): Automatically convert strings to target types (numbers, booleans, integers)- Default:
falsefor SchemaObject,truefor ParameterObject
- Default:
setDefault(boolean): Apply default values from the schemaremoveAdditional(boolean): Remove properties not defined in the schemacontext('read' | 'write'): Validation context for readOnly/writeOnly properties
ParameterObject Specific:
parseStyle(boolean): Enable/disable automatic parameter style parsing (default:true)contentType(string): Specify content type for parameter content validation
Example:
import { StaticSchemaObject } from 'openapi-police';
const schema = new StaticSchemaObject({
type: 'object',
properties: {
age: { type: 'integer' },
name: { type: 'string', default: 'Anonymous' }
}
});
// With type coercion and defaults
const data = await schema.validate(
{ age: '25' },
{ coerceTypes: true, setDefault: true }
);
console.log(data); // { age: 25, name: 'Anonymous' }Troubleshooting
Common Issues
"style" Error with ParameterObject
- This typically means you're using an incompatible style/type combination
- Check the Style Support table to see which styles are supported for your parameter location
- Verify your parameter schema type matches what the style expects
Type Validation Errors
- By default, SchemaObject does NOT coerce types
- For ParameterObject, set
coerceTypes: truein options if you need string→number conversion - Remember: ParameterObject automatically enables
coerceTypesby default
"Cannot find module" Errors
- Make sure you're importing from 'openapi-police' not './dist/index.js'
- Check that you're using ESM imports (
import) not CommonJS (require)
Discriminator Validation Fails
- Ensure your discriminator propertyName exists in the data
- Verify the discriminator value matches one of the mapped schemas
- For $ref resolution, consider using
jsonrefto parse your schemas first
Performance Tips
- Reuse validator instances - Create validators once and reuse them
- Leverage caching - Use shared registries for external schema references
- Validate early - Validate parameters and request bodies before processing
- Use appropriate styles - Choose the most efficient parameter style for your use case
Browser Support
openapi-police works in all modern browsers and Node.js environments. It requires:
- ES2015+ support
- Promise support
- JSON.parse/JSON.stringify
License
MIT License - see the LICENSE file for details.
Contributing
Contributions are welcome! Please ensure all tests pass:
pnpm install
pnpm test
pnpm run coverage