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
$idproperty 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
messagebuilt from Ajv errors - An
errorsfield containing the raw AjvErrorObject[]
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"
}
}