import { validate } from 'class-validator';
import type { ClassConstructor } from 'class-transformer';
import { createClientValidation, HttpException, HttpStatus } from 'vovk';
const canValidate = (inputObject: unknown, validationSchema: unknown): inputObject is ClassConstructor<object> => {
return (
!!inputObject &&
typeof inputObject === 'object' &&
inputObject?.constructor !== Object &&
!(inputObject instanceof FormData) &&
!!validationSchema &&
typeof validationSchema === 'object' &&
'x-isDto' in validationSchema
);
};
export const validateOnClient = createClientValidation({
validate: async (input, schema, meta) => {
if (canValidate(input, schema)) {
const errors = await validate(input, {
whitelist: true,
forbidNonWhitelisted: true,
validationError: { target: false, value: false },
});
if (errors.length > 0) {
const err = errors.map((e) => Object.values(e.constraints || {}).join(', ')).join(', ');
throw new HttpException(HttpStatus.NULL, `DTO validation failed. Invalid ${meta.type} on client: ${err}`, {
[meta.type]: input,
validationErrors: errors,
endpoint: meta.endpoint,
});
}
}
return input;
},
});
Client-side validation with Ajv
All server-side validation libraries suppose to emit JSON schemas that can be used for client-side validation and other features, such as OpenAPI documentation, code generation and LLM Function Calling. The client-side validation is performed automatically with a little set up. The main library that implements the client-side validation is vovk-ajvβ that uses Ajvβ to validate emitted JSON schemas. It is installed and set up automatically when npx vovk-init
is used, in other cases you can install it manually and change vovk.config to use it as described below.
npm install vovk-ajv
Then update the vovk.config.mjs
file to use it:
/** @type {import('vovk').VovkConfig} */
const config = {
imports: {
validateOnClient: 'vovk-ajv',
},
};
export default config;
The field config.imports.validateOnClient
is used to specify the path to the validateOnClient
function that is imported by the generated TypeScript client and passed to createRPC
function that initializes an RPC module.
The library can be configured by adjusting config.libs.ajv
field in the vovk.config.mjs
file to set Ajv options, localization and target version of the JSON schema. The type VovkAjvConfig
should be set explicitly since vovk-ajv
is a separate package and the type is not imported automatically.
/** @type {import('vovk').VovkConfig} */
const config = {
imports: {
validateOnClient: 'vovk-ajv',
},
libs: {
/** @type {import('vovk-ajv').VovkAjvConfig} */
ajv: {
options: {
// Ajv options
strict: false,
},
localize: 'en', // language supported by "ajv-i18n"
target: 'draft-07', // auto-detected from $schema but can be configured: 'draft-2020-12' | 'draft-07'
},
},
};
export default config;