class-validator في TypeScript: دليل شامل مع أمثلة

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

مقدمة عن class-validator

class-validator هي مكتبة قوية وشائعة للتحقق من صحة البيانات في TypeScript و JavaScript، مبنية على أساس الديكوراتورات (decorators). تتيح لك وصف قواعد التحقق بشكل تصريحي داخل الكلاسات باستخدام التعليقات التوضيحية. وهذا يجعل الكود نظيفًا وقابلاً للقراءة وسهل الصيانة.

هذه المكتبة مفيدة بشكل خاص عند تطوير تطبيقات الخادم (مثل NestJS أو Express)، حيث تحتاج إلى التحقق من صحة الطلبات الواردة وبيانات النماذج والإعدادات وأي هياكل بيانات أخرى. بدلاً من كتابة سلاسل طويلة من if-else، يمكنك ببساطة إضافة ديكوراتورات إلى خصائص الكلاس.

لماذا تحتاج class-validator؟

  • التصريحية: يتم تعريف قواعد التحقق بجانب البيانات، مما يحسن readability.
  • مجموعة قوية من المدققات: تتضمن عشرات الديكوراتورات المدمجة للتحقق من النصوص والأرقام والتواريخ والمصفوفات والكائنات المتداخلة وغيرها.
  • مدققات مخصصة: تتيح لك إنشاء قواعد التحقق الخاصة بك بسهولة.
  • التكامل مع TypeScript: دعم كامل للأنواع والديكوراتورات.
  • التحقق غير المتزامن: تدعم عمليات التحقق التي تتطلب عمليات غير متزامنة (مثل التحقق من uniqueness في قاعدة البيانات).

التثبيت

لاستخدام class-validator، ستحتاج إلى Node.js و npm/yarn. قم بتثبيت المكتبة والاعتماديات اللازمة:

npm install class-validator class-transformer reflect-metadata

أو عبر yarn:

yarn add class-validator class-transformer reflect-metadata

ملاحظة هامة: مكتبة reflect-metadata إلزامية لعمل الديكوراتورات. تأكد من استيرادها في ملفك الرئيسي (مثل index.ts أو main.ts):

import 'reflect-metadata';

تأكد أيضًا من تفعيل الإعدادات التالية في ملف tsconfig.json:

{
  "compilerOptions": {
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true
  }
}

بداية سريعة

لنقم بإنشاء نموذج بسيط لمستخدم والتحقق من صحته.

import 'reflect-metadata';
import { validate, IsString, IsEmail, MinLength } from 'class-validator';
import { plainToClass } from 'class-transformer';

class User {
    @IsString()
    @MinLength(3, { message: 'يجب أن يحتوي الاسم على 3 أحرف على الأقل' })
    name: string;

    @IsEmail({}, { message: 'البريد الإلكتروني غير صحيح' })
    email: string;

    constructor(name: string, email: string) {
        this.name = name;
        this.email = email;
    }
}

async function main() {
    // إنشاء كائن ببيانات غير صحيحة
    const userData = { name: 'Jo', email: 'not-an-email' };
    
    // تحويل الكائن العادي إلى نسخة من الكلاس
    const user = plainToClass(User, userData);
    
    // التحقق من الصحة
    const errors = await validate(user);
    
    if (errors.length > 0) {
        console.log('أخطاء التحقق:', errors);
        errors.forEach(error => {
            console.log(`الحقل: ${error.property}`);
            for (const constraint in error.constraints) {
                console.log(`  - ${error.constraints[constraint]}`);
            }
        });
    } else {
        console.log('تم التحقق بنجاح!');
    }
}

main();
// الإخراج:
// أخطاء التحقق: [...]
// الحقل: name
//   - يجب أن يحتوي الاسم على 3 أحرف على الأقل
// الحقل: email
//   - البريد الإلكتروني غير صحيح

الطرق والديكوراتورات الأساسية

1. @IsString()

الوظيفة: تتحقق مما إذا كانت القيمة نصًا (string).

المعلمات: تقبل كائنًا اختياريًا يحتوي على خيارات التحقق (مثل { message: '...' }).

class Product {
    @IsString()
    title: string;
}

2. @IsNumber()

الوظيفة: تتحقق مما إذا كانت القيمة رقمًا.

المعلمات: تقبل كائنًا اختياريًا مع خيارات مثل allowNaN (السماح بـ NaN) و allowInfinity.

class Product {
    @IsNumber()
    price: number;
}

3. @MinLength(min) و @MaxLength(max)

الوظيفة: تتحقق من أن طول النص لا يقل عن min ولا يزيد عن max.

class Post {
    @MinLength(10)
    @MaxLength(500)
    content: string;
}

4. @IsEmail()

الوظيفة: تتحقق من صحة تنسيق البريد الإلكتروني.

class Contact {
    @IsEmail()
    email: string;
}

5. @IsArray() و @ArrayMinSize(size)

الوظيفة: تتحقق من أن القيمة مصفوفة وأن حجمها لا يقل عن size.

class Order {
    @IsArray()
    @ArrayMinSize(1)
    items: string[];
}

6. @IsOptional()

الوظيفة: تجعل الخاصية اختيارية، أي يتم تخطي التحقق إذا كانت القيمة غير محددة (undefined) أو فارغة (null).

class Profile {
    @IsOptional()
    @IsString()
    bio?: string;
}

7. @ValidateNested()

الوظيفة: تتحقق من صحة الكائنات المتداخلة (nested objects). يجب استخدامها مع @Type() من مكتبة class-transformer.

import { Type } from 'class-transformer';

class Address {
    @IsString()
    city: string;
}

class User {
    @ValidateNested()
    @Type(() => Address)
    address: Address;
}

إنشاء مدقق مخصص

يمكنك إنشاء ديكوراتور مخصص باستخدام ValidatorConstraint و registerDecorator:

import { ValidatorConstraint, ValidatorConstraintInterface, ValidationArguments, registerDecorator, ValidationOptions } from 'class-validator';

@ValidatorConstraint({ name: 'isLongerThan', async: false })
export class IsLongerThanConstraint implements ValidatorConstraintInterface {
    validate(value: any, args: ValidationArguments) {
        const [relatedPropertyName] = args.constraints;
        const relatedValue = (args.object as any)[relatedPropertyName];
        return typeof value === 'string' && typeof relatedValue === 'string' && value.length > relatedValue.length;
    }

    defaultMessage(args: ValidationArguments) {
        const [relatedPropertyName] = args.constraints;
        return `$property يجب أن يكون أطول من ${relatedPropertyName}`;
    }
}

export function IsLongerThan(property: string, validationOptions?: ValidationOptions) {
    return function (object: Object, propertyName: string) {
        registerDecorator({
            target: object.constructor,
            propertyName: propertyName,
            options: validationOptions,
            constraints: [property],
            validator: IsLongerThanConstraint,
        });
    };
}

// الاستخدام:
class Post {
    @IsString()
    title: string;

    @IsString()
    @IsLongerThan('title', { message: 'يجب أن يكون المحتوى أطول من العنوان' })
    content: string;
}

التحقق غير المتزامن

يمكنك إنشاء مدقق غير متزامن للتحقق من البيانات في قاعدة البيانات:

@ValidatorConstraint({ name: 'isEmailUnique', async: true })
export class IsEmailUniqueConstraint implements ValidatorConstraintInterface {
    async validate(email: string) {
        // افترض وجود دالة للتحقق من البريد في قاعدة البيانات
        const user = await findUserByEmail(email);
        return !user; // يعيد true إذا كان البريد غير موجود
    }

    defaultMessage() {
        return 'البريد الإلكتروني موجود بالفعل';
    }
}

export function IsEmailUnique(validationOptions?: ValidationOptions) {
    return function (object: Object, propertyName: string) {
        registerDecorator({
            target: object.constructor,
            propertyName: propertyName,
            options: validationOptions,
            constraints: [],
            validator: IsEmailUniqueConstraint,
        });
    };
}

الخلاصة

مكتبة class-validator هي أداة لا غنى عنها لأي مطور TypeScript. فهي تبسط عملية التحقق من صحة البيانات، وتجعل الكود أكثر وضوحًا وقابلية للصيانة. سواء كنت تعمل على تطبيق خادم صغير أو مشروع كبير، فإن class-validator ستوفر لك الوقت والجهد.

ابدأ باستخدامها اليوم في مشاريعك، وستلاحظ الفرق في جودة وسرعة تطويرك!

توصيات