NestJS — Node.js للمؤسسات

الأنابيب: التحويل والتحقّق

16 دقيقة الدرس 11 من 30

الأنابيب: التحويل والتحقّق

يقع الأنبوب (pipe) بين الطلب الوارد ومعالجك، مؤدّيًا إحدى مهمّتين: التحويل (تحويل المدخل إلى الشكل المطلوب) أو التحقّق (فحص المدخل ورمي خطأ إن كان غير صالح). إنّ ValidationPipe من الدرس السابق ليس سوى أنبوب مدمج واحد — والآن لنفهم الآليّة.

الأنابيب المدمجة

يأتي NestJS بعدّة أنابيب جاهزة. وأشيعها يحوّل معاملات المسار النصّية إلى قيم مكتوبة الأنواع:

import { Controller, Get, Param, ParseIntPipe } from '@nestjs/common'; @Controller('users') export class UsersController { @Get(':id') findOne(@Param('id', ParseIntPipe) id: number) { // id رقم حقيقي؛ والقيمة غير الرقمية ترمي 400 تلقائيًا return this.usersService.findOne(id); } }

تشمل المدمجات الأخرى ParseUUIDPipe وParseBoolPipe وParseArrayPipe وParseEnumPipe وDefaultValuePipe.

تذكّر: تصل معاملات المسار وسلاسل الاستعلام دائمًا كنصوص. يحوّل ParseIntPipe القيمة '42' إلى الرقم 42 — ويرفض 'abc' بـ 400 قبل تشغيل معالجك.

أين يمكن تطبيق الأنابيب

  • مستوى المعامل — على @Param()/@Body() واحد (الأدق).
  • مستوى الدالّة — بـ @UsePipes() على المعالج.
  • عامapp.useGlobalPipes() في main.ts (يُطبَّق في كل مكان).

كتابة أنبوب مخصّص

الأنبوب المخصّص يُنفّذ PipeTransform ودالّة transform(value, metadata) واحدة. ما يُعيده يصبح وسيط المعالج؛ وما يرميه يصبح استجابة الخطأ:

import { PipeTransform, Injectable, BadRequestException } from '@nestjs/common'; @Injectable() export class TrimPipe implements PipeTransform { transform(value: any) { if (typeof value !== 'string') { throw new BadRequestException('Expected a string'); } return value.trim(); } } // الاستخدام @Post() create(@Body('name', TrimPipe) name: string) {}

وسيط البيانات الوصفية

يُخبر المعامل الثاني الأنبوبَ بماذا يعالج — النوع (body أو query أو param)، والنوع الفوقي المتوقَّع، واسم المعامل. وهكذا يعرف أنبوب عام كـ ValidationPipe أي صنف DTO يتحقّق منه:

transform(value: any, metadata: ArgumentMetadata) { // metadata.type -> 'body' | 'query' | 'param' | 'custom' // metadata.metatype -> الصنف المتوقَّع (مثل CreateUserDto) // metadata.data -> مفتاح @Body('name'), إن وُجد return value; }
تعمل الأنابيب قبل المعالج، بالترتيب. قد يحمل المعامل عدّة أنابيب؛ تُنفَّذ من اليسار إلى اليمين، كلٌّ يستقبل ناتج سابقه — فيمكنك التحويل ثم التحقّق في سلسلة.
أبقِ الأنابيب مركّزة. يجب أن يُحوّل الأنبوب أو يتحقّق من مدخل واحد — لا أن يؤدّي منطق عمل أو استعلامات قاعدة بيانات أو آثارًا جانبية. تلك من شأن الخدمات.

الخلاصة

تُحوّل الأنابيب المدخلات أو تتحقّق منها قبل وصولها إلى معالجك. استخدم أنابيب مدمجة كـ ParseIntPipe لتحويل معاملات المسار وحراستها، واكتب أنابيب مخصّصة بتنفيذ PipeTransform.transform(). يتيح وسيط metadata للأنابيب العامة فحص ما تعالجه. تُربَط الأنابيب على مستوى المعامل أو الدالّة أو عام. تاليًا: الوسيطات، التي تعمل أبكر في دورة الحياة.