Skip to content

vivocha/openapi-police

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

53 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

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.

npm version CI Coverage Status

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-police

Quick 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 schema
  • removeAdditional (boolean): Remove properties not in schema
  • context ('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 ValidationError

ParameterObject

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 coerceTypes by default
    • Automatically parses style-encoded strings based on parameter schema type
    • Returns the validated and parsed data

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 parameters
  • cookie - 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 Object
  • contentType (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-separated

OpenAPI 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 ones

Parameter 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: false for SchemaObject, true for ParameterObject
  • setDefault (boolean): Apply default values from the schema
  • removeAdditional (boolean): Remove properties not defined in the schema
  • context ('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: true in options if you need string→number conversion
  • Remember: ParameterObject automatically enables coerceTypes by 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 jsonref to parse your schemas first

Performance Tips

  1. Reuse validator instances - Create validators once and reuse them
  2. Leverage caching - Use shared registries for external schema references
  3. Validate early - Validate parameters and request bodies before processing
  4. 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

About

OpenAPI v3 validators and utilities

Resources

Stars

Watchers

Forks

Packages

No packages published

Contributors 4

  •  
  •  
  •  
  •