Skip to main content

API

AjvValidationPipe

Validates values using Ajv JSON Schema validator. Register it globally to validate incoming HTTP parameters (e.g., body) in your controllers.

constructor(ajv: Ajv, schemaList?: AnySchema[])

Parameters

  • ajv (Ajv): The Ajv instance to use for validation. You can configure it with custom formats, keywords, and options.
  • schemaList (optional AnySchema[]): One or more JSON schemas applied globally to every validated value. These run before any parameter-level schemas added via @ValidateAjvSchema.

AjvValidationPipe can be registered as a global pipe: adapter.useGlobalPipe(new AjvValidationPipe(ajv)). This way, controller parameters decorated with @ValidateAjvSchema are automatically validated.

Example: register an AjvValidationPipe globally

import { AjvValidationPipe } from '@inversifyjs/ajv-validation';
import { InversifyExpressHttpAdapter } from '@inversifyjs/http-express';
import { InversifyValidationErrorFilter } from '@inversifyjs/http-validation';
import Ajv from 'ajv';
import { Container } from 'inversify';

const container: Container = new Container();

const ajv: Ajv = new Ajv();

const adapter: InversifyExpressHttpAdapter = new InversifyExpressHttpAdapter(
container,
{ logger: true },
);
adapter.useGlobalFilters(InversifyValidationErrorFilter);
adapter.useGlobalPipe(new AjvValidationPipe(ajv));

AjvCompiledValidationPipe

An optimized version of AjvValidationPipe that uses compiled Ajv validation functions for better performance. Best suited for production environments where schemas don't change frequently.

constructor(ajv: Ajv, schemaList?: AnySchema[])

Parameters

  • ajv (Ajv): The Ajv instance to use for validation. Schemas must have an $id property to be compiled.
  • schemaList (optional AnySchema[]): One or more JSON schemas applied globally to every validated value.

Important: When using AjvCompiledValidationPipe, all schemas must have an $id property. The pipe compiles and caches validation functions for better performance.

Example: register an AjvCompiledValidationPipe globally

import { AjvCompiledValidationPipe } from '@inversifyjs/ajv-validation';
import { InversifyExpressHttpAdapter } from '@inversifyjs/http-express';
import { InversifyValidationErrorFilter } from '@inversifyjs/http-validation';
import Ajv from 'ajv';
import { Container } from 'inversify';

const container: Container = new Container();

const ajv: Ajv = new Ajv();

const adapter: InversifyExpressHttpAdapter = new InversifyExpressHttpAdapter(
container,
{ logger: true },
);
adapter.useGlobalFilters(InversifyValidationErrorFilter);
adapter.useGlobalPipe(new AjvCompiledValidationPipe(ajv));

ValidateAjvSchema

Attaches one or more JSON schemas to a specific parameter (e.g., @Body()), so the AjvValidationPipe or AjvCompiledValidationPipe can validate the value before your controller method runs.

ValidateAjvSchema(...schemaList: AnySchema[]): ParameterDecorator

Parameters

  • schemaList: One or more JSON Schema objects that define the expected structure and constraints for the parameter value.

When multiple schemas are provided, they run in sequence. The value must pass all schema validations. Any schemas provided to AjvValidationPipe run first; parameter-level schemas run afterwards.

On validation failure, an InversifyAjvValidationError is thrown and can be converted to a Bad Request HTTP response by InversifyValidationErrorFilter.

Example: validate request body with JSON Schema

import { ValidateAjvSchema } from '@inversifyjs/ajv-validation';
import { Body, Controller, Post } from '@inversifyjs/http-core';
import { AnySchema } from 'ajv';

interface User {
name: string;
email: string;
age?: number;
}

const userSchema: AnySchema = {
additionalProperties: false,
properties: {
age: { minimum: 0, type: 'number' },
email: { format: 'email', type: 'string' },
name: { minLength: 1, type: 'string' },
},
required: ['name', 'email'],
type: 'object',
};

@Controller('/users')
export class UserController {
@Post()
public async createUser(
@Body()
@ValidateAjvSchema(userSchema)
user: User,
): Promise<User> {
return user;
}
}

Error Handling

When validation fails, the pipe throws an InversifyAjvValidationError (a subclass of InversifyValidationError) with:

  • A user-friendly message built from Ajv errors
  • An errors field containing the raw Ajv ErrorObject[]

Ajv error details include:

  • Property path where validation failed
  • Expected vs actual values
  • Validation rule that was violated

Use InversifyValidationErrorFilter (from @inversifyjs/http-validation) or a custom @CatchError(InversifyValidationError) / @CatchError(InversifyAjvValidationError) filter to convert these errors into HTTP 400 responses.

Customizing validation error responses

InversifyValidationErrorFilter returns { message } by default. When frontends need field-level errors, catch InversifyAjvValidationError and map its errors field into the response body you want:

import { InversifyAjvValidationError } from '@inversifyjs/ajv-validation';
import {
BadRequestHttpResponse,
CatchError,
type ErrorFilter,
} from '@inversifyjs/http-core';
import { InversifyValidationErrorKind } from '@inversifyjs/validation-common';
import { type ErrorObject } from 'ajv';

function mapAjvErrors(errors: Partial<ErrorObject>[]): Record<string, string> {
const result: Record<string, string> = {};

for (const error of errors) {
const missingProperty: unknown = error.params?.['missingProperty'];
const path: string =
error.instancePath === undefined || error.instancePath === ''
? typeof missingProperty === 'string'
? missingProperty
: ''
: error.instancePath.replace(/^\//, '').replaceAll('/', '.');

if (
path !== '' &&
error.message !== undefined &&
result[path] === undefined
) {
result[path] = error.message;
}
}

return result;
}

@CatchError(InversifyAjvValidationError)
export class CustomAjvValidationErrorFilter implements ErrorFilter<InversifyAjvValidationError> {
public catch(error: InversifyAjvValidationError): never {
switch (error.kind) {
case InversifyValidationErrorKind.validationFailed:
throw new BadRequestHttpResponse(
{
errors: mapAjvErrors(error.errors ?? []),
message: 'Validation failed',
success: false,
},
error.message,
{
cause: error,
},
);
default:
throw new Error(error.message, {
cause: error,
});
}
}
}

Register the filter instead of (or in addition to) the built-in one:

adapter.useGlobalFilters(CustomAjvValidationErrorFilter);

That yields responses like:

{
"success": false,
"message": "Validation failed",
"errors": {
"firstName": "must NOT have fewer than 1 characters",
"lastName": "must NOT have fewer than 1 characters"
}
}