# التحقق

> هذه ترجمة للصفحة الأصلية بالإنجليزية. عند وجود أي اختلاف، تُعتمد [النسخة الإنجليزية](/docs/validation).

يتحقق DaloyJS من المدخلات عبر [Standard Schema](https://github.com/standard-schema/standard-schema)، وهي واجهة صغيرة تعرضها أدوات تحقق مثل **Zod** و**Valibot** و**ArkType**، وكذلك TypeBox عبر محوّل Standard Schema. اختر أداة التحقق التي تناسب مشروعك؛ فعقد DaloyJS واحد في كل الحالات.

**Diagram: واجهة واحدة وأربع أدوات تحقق**

- **Standard Schema** (source, عقد الإطار) - الخاصية ~standard
- **Zod** (الافتراضي) - z.object({ ... })
- **Valibot** (قابل لإزالة الشيفرة غير المستخدمة) - v.object({ ... })
- **ArkType** - type({ ... })
- **TypeBox** - عبر محوّل

تعرض كل أداة تحقق الخاصية ~standard نفسها، لذا يستنتج DaloyJS أنواع المعالج، ويولّد OpenAPI، ويعيد أخطاء problem+json عبر مسار واحد في الإطار.

## ما الذي يُتحقق منه

يمكنك لكل مسار التصريح بمخططات لما يلي:

- `request.params`: معاملات المسار المفكوكة الترميز. تبدأ كنصوص؛ حوّلها في مخططك إن أردت أرقامًا أو UUID أو قيمًا تعدادية.
- `request.query`: قيم سلسلة الاستعلام. تصبح المفاتيح المكررة مصفوفات قبل التحقق.
- `request.headers`: ترويسات الطلب بأسماء بأحرف صغيرة.
- `request.body`: أجسام الطلبات المحللة. لا يُقرأ الجسم إلا عندما يصرّح المسار بمخطط للجسم.
- `responses[status].body`: أجسام استجابة مكتوبة الأنواع ومتحقق منها.

تُسقط سلاسل الاستعلام وحقول النماذج وحقول multipart مفاتيح تلويث النماذج الأولية مثل `__proto__` و`constructor` و`prototype` قبل التحقق.

## قيّد الحقول الرقمية (المال والكميات والمعرّفات)

يقبل `z.number()` المجرد القيمة `1e308`، و`-Infinity` بعد أخطاء التحويل، وقيمًا أخرى سليمة لأعداد IEEE العشرية لكنها كارثية للمال أو المخزون أو ترقيم الصفحات. ضع دائمًا حدودًا خاصة بالمجال على حقول الطلب الرقمية:

```ts
import { z } from "zod";

// المال: قيمة منتهية وموجبة ومحدودة بسقف تجاري.
const Money = z.number().finite().positive().max(1_000_000);

// الكميات: أعداد صحيحة في نطاق معقول.
const Qty = z.number().int().positive().max(10_000);

// استخدم coerce فقط عندما تصل القيمة نصًا (استعلام أو نموذج).
const Page = z.coerce.number().int().min(1).max(1000);

// الأجسام الصارمة ترفض الحقول الإضافية في الإسناد الجماعي.
const CreatePayment = z
  .object({ amount: Money, currency: z.enum(["USD", "EUR", "NOK"]) })
  .strict();
```

يرفض Zod 4 بالفعل `NaN` والأرقام غير المنتهية في `z.number()` في حالات كثيرة، لكن السقف (`.max()`) وقيد الإشارة (`.positive()` / `.nonnegative()`) يظلان خط دفاعك الأخير ضد احتيال المبالغ المستردة والفيض العددي. اقرن ذلك بمخططات الاستجابة حتى لا تتسرب الحقول الحساسة إلى الخارج أبدًا.

## مثال متكامل

```ts
import { App } from "@daloyjs/core";
import { z } from "zod";

const CreateLineItem = z.object({
  sku: z.string().min(1),
  qty: z.coerce.number().int().positive().max(10_000),
});

const LineItem = z.object({
  id: z.string(),
  tenantId: z.string(),
  sku: z.string(),
  qty: z.number().int().positive(),
  dryRun: z.boolean(),
});

export const app = new App().post(
  "/orders/:orderId/items",
  {
    operationId: "createOrderItem",
    request: {
      params: z.object({ orderId: z.uuid() }),
      query: z
        .object({
          dryRun: z
            .enum(["true", "false"])
            .transform((value) => value === "true")
            .optional(),
        })
        .optional(),
      headers: z.object({ "x-tenant": z.string().min(1) }),
      body: CreateLineItem,
    },
    responses: {
      201: { description: "Created", body: LineItem },
      422: { description: "Validation error" },
    },
  },
  async ({ params, query, headers, body }) => {
    const tenantId = headers["x-tenant"];
    const dryRun = query?.dryRun ?? false;

    return {
      status: 201,
      body: {
        id: `${params.orderId}:li_1`,
        tenantId,
        ...body,
        dryRun,
      },
    };
  },
);
```

## اختر أداة التحقق

- [Zod](/docs/validation/zod) (بالإنجليزية): الخيار الافتراضي لمعظم الفرق. واجهة قابلة للتسلسل، ومنظومة كبيرة، وسهلة التعلم.
- [Valibot](/docs/validation/valibot) (بالإنجليزية): معيارية وقابلة لإزالة الشيفرة غير المستخدمة. ممتازة لبيئات الحافة (edge) وللعقود التي تُشحن إلى المتصفح.

تعمل أيضًا المخططات المتوافقة مع ArkType وTypeBox عندما تعرض الخاصية `~standard` نفسها، لكن DaloyJS لا يوفّر توثيقًا وقوالب رسمية إلا لـ Zod وValibot.

## مقارنة جنبًا إلى جنب

```ts
// Zod
import { z } from "zod";
const Body = z.object({
  sku: z.string(),
  qty: z.number().int().positive(),
});

// Valibot
import * as v from "valibot";
const Body = v.object({
  sku: v.string(),
  qty: v.pipe(v.number(), v.integer(), v.minValue(1)),
});

// ArkType
import { type } from "arktype";
const Body = type({ sku: "string", qty: "1<=number.integer" });

// TypeBox
import { Type } from "@sinclair/typebox";
const Body = Type.Object({ sku: Type.String(), qty: Type.Integer({ minimum: 1 }) });
// غلّفه بمحوّل Standard Schema الخاص بمشروعك قبل تمريره إلى DaloyJS.
```

بمجرد أن يعرض المخطط `~standard`، يستنتج DaloyJS أنواع المعالج، ويولّد OpenAPI، ويعيد أخطاء problem+json بالطريقة نفسها أيًا كانت أداة التحقق التي أنتجته.

## الأخطاء

**Diagram: حلّل، ثم تفرّع حسب النتيجة**

- **الطلب الخام** (غير موثوق) - params · query · headers · body
- **تحليل المخطط** (standard schema) - validate(schema, input)
- **422 problem+json** (عند الفشل) - errors: [{ path, message }]
- **معالج مكتوب الأنواع** (عند النجاح) - ctx.body / params / query / headers

يعمل كل مخطط مصرّح به قبل معالجك. المدخلات غير الصالحة لا تصل أبدًا إلى شيفرة المعالج، بل تنتهي مبكرًا باستجابة 422 وفق RFC 9457. أما المدخلات الصالحة فتصل مكتوبة الأنواع بالكامل.

عند المدخلات غير الصالحة، يعيد DaloyJS **422 Unprocessable Entity** بصيغة problem+json وفق RFC 9457، مع مصفوفة `errors` تحتوي على سجلات `path` و`message` لكل مشكلة. لا تكتب معالج أخطاء لهذا، فهو مدمج. راجع [الأخطاء وproblem+json](/ar/docs/errors).

## التحقق من الاستجابات

عند التصريح بمخطط استجابة، يتحقق DaloyJS من القيمة التي يعيدها المعالج قبل تسلسلها. والقيمة المتحقق منها هي ما يُرسل عبر الشبكة، لذا فإن مخططات الكائنات التي تحذف المفاتيح غير المعروفة تمنع أيضًا تسرب الحقول غير المصرّح بها إلى العملاء. إذا فشل التحقق، يتلقى العميل في الإنتاج استجابة مشكلة 500 منقّحة.

## حدود الجسم وأنواع المحتوى

يحدّ خيار التطبيق `bodyLimitBytes` (الافتراضي 1 MiB) من قيمة Content-Length المعلنة ومن الحجم المتدفق لكل جسم طلب -> **413**. ومنذ 1.5.4 ينطبق هذا على كل مسار، بما في ذلك الأجسام التي يقرؤها المعالج بنفسه من `ctx.request`؛ ويُفحص الحد بشكل كسول، عند قراءة الجسم.

عندما يصرّح المسار بـ `request.body`، يفرض DaloyJS أيضًا ما يلي:

- مطابقة Content-Type لقائمة `accepts` الخاصة بالمسار، أو لـ `allowedContentTypes` العامة إن ضُبطت -> **415**.
- أنواع الأجسام المقبولة افتراضيًا: `application/json` و`application/x-www-form-urlencoded` و`multipart/form-data`.
- تحليل آمن من تلويث النماذج الأولية لـ JSON وسلاسل الاستعلام ونماذج urlencoded ونماذج multipart.

تُحلَّل أجسام JSON إلى كائنات، وأجسام urlencoded عبر `URLSearchParams`، وأجسام multipart عبر واجهة المنصة `Request.formData()`. وإذا فعّلت لمسار ما نوع وسائط نصيًا مخصصًا، يمرر DaloyJS النص المفكوك إلى مخطط الجسم لديك.

```ts
app.post(
  "/legacy-form",
  {
    operationId: "legacyForm",
    accepts: ["application/x-www-form-urlencoded"],
    request: {
      body: z.object({
        email: z.email(),
        qty: z.coerce.number().int().positive(),
      }),
    },
    responses: {
      200: { description: "ok", body: z.object({ ok: z.boolean() }) },
    },
  },
  async ({ body }) => ({ status: 200, body: { ok: body.qty > 0 } }),
);
```

## المزج بين أدوات التحقق

يمكنك المزج بينها لكل مسار. مخطط Zod في ملف ومخطط Valibot في ملف آخر كلاهما صالح، وهذا مفيد عند ترحيل قاعدة شيفرة قائمة أو عند استهلاك مخططات من حزمة مشتركة.

آخر تحديث: 30 أكتوبر 2026

---

Source: https://daloyjs.dev/ar/docs/validation