My Class Validator
Pourquoi choisir entre la Performance (JSON Schema) et la Lisibilité (Decorators) ? my-class-validator réunit le meilleur des deux mondes.
🚀 Le concept
Contrairement à class-validator qui utilise la réflexion (reflection) à chaque requête (lent), cette librairie transforme vos classes TypeScript en JSON Schema standard au démarrage de l'application.
Ce schéma est ensuite compilé par AJV (Fastify) pour une validation ultra-rapide (JIT).
📦 Installation
Vous avez besoin du package et de reflect-metadata.
npm install my-class-validator reflect-metadata
⚡️ Utilisation Recommandée
La bonne pratique consiste à définir la classe et à exporter son schéma dans le même fichier.
1. Définir et Exporter le DTO (create-user.dto.ts)
Vous définissez vos décorateurs, puis vous appelez generateSchema à la fin du fichier.
import { IsString, IsInt, IsRequired, generateSchema } from 'my-class-validator';
export class CreateUserDto {
@IsString({ message: "Le nom doit être une chaîne de caractères" })
@IsRequired()
username: string;
@IsInt()
@IsRequired()
age: number;
}
// ✅ On exporte le Schéma directement ici
export const CreateUserSchema = generateSchema(CreateUserDto);
2. Utiliser avec my-fastify-decorators
Si vous utilisez my-fastify-decorators, l'intégration se fait via le décorateur @BodySchema. Il suffit d'importer le schéma généré précédemment.
import { Controller, Post, Body, BodySchema } from 'my-fastify-decorators';
// On importe la Classe (pour le typage) ET le Schéma (pour la validation)
import { CreateUserDto, CreateUserSchema } from './dto/create-user.dto';
@Controller('/users')
export class UserController {
@Post()
@BodySchema(CreateUserSchema) // <--- On passe le schéma pré-généré
create(@Body() body: CreateUserDto) {
// Ici, body est typé ET validé
return { status: 'ok', user: body.username };
}
}
3. Résultat généré
Voici ce que CreateUserSchema contient réellement (et ce que Fastify reçoit) :
{
type: 'object',
required: ['username', 'age'],
properties: {
username: {
type: 'string',
errorMessage: { type: 'Le nom doit être une chaîne de caractères' }
},
age: { type: 'integer' }
}
}