الـ type: سبع أنواع بس
الـ JSON فيه ٦ أنواع، والـ JSON Schema زوّدت integer:
| الـ type | بيقبل | بيرفض |
|---|---|---|
string | "كشري" | 5، null |
number | 5، 5.5، -2e3 | "5" |
integer | 5، 5.0 | 5.5، "5" |
boolean | true، false | "true"، 0 |
null | null | ""، 0 |
object | {"a": 1} | []، null |
array | [1, 2] | {} |
- الـ
integerبالقيمة مش بالشكل:5.0بيعدّي كـ integer لأنه رياضيًا رقم صحيح. اتجرّبت في Ajv وطلعتtrue. - الـ
nullنوع لوحده:"type": "string"مش بيقبلnull. لو عايز تسمح بيه لازم تقول كده صراحة. - الـ array مش object: مع إن
typeof [] === "object"في JavaScript، الـ JSON Schema بتفرق بينهم.
أكتر من type
اكتب type كـ array:
nullable.json
{ "type": ["string", "null"] }- بيقبل:
"Saeed"وnull. - بيرفض:
5برسالةmust be string,null. - ده بديل
nullable: true: اللي كان في OpenAPI 3.0. في JSON Schema الحقيقية مفيش keyword اسمهnullable(فصل ١١).
الـ properties والـ required
product.json
{ "type": "object", "properties": { "name": { "type": "string" }, "price": { "type": "number" }, "inStock": { "type": "boolean" } }, "required": ["name", "price"]}- الـ
properties: بتوصف كل حقل لو موجود. حقل مش موجود؟ مفيش مشكلة. - الـ
required: array بأسماء الحقول اللي لازم تبقى موجودة. Ajv بيرد بـmust have required property 'price'. - موجود يعني موجود:
"price": nullيعتبر موجود، وبعدين بيفشل في الـtype. والـrequiredمش بيمنع""ولا0. - الـ
requiredبره الـproperties: غلطة مشهورة إنك تكتب"required": trueجوه الحقل. ده كان draft-03 وخلاص مبقاش موجود.
الـ additionalProperties: الحقول الزيادة
الـ default إن أي حقل زيادة مسموح. ده خطر لما الـ body بيتحفظ زي ما هو:
mass-assignment.js
// اليوزر بعت { "name": "Saeed", "isAdmin": true }await db.users.update(id, req.body); // 😱 بقى adminعندك ٣ اختيارات:
false: اقفل الباب
"additionalProperties": false ← أي حقل مش في properties (أو patternProperties) يفشّل الـ validation برسالة must NOT have additional properties، والـ params.additionalProperty فيه اسمه.
schema: اسمح بشرط
"additionalProperties": { "type": "string" } ← الحقول الزيادة مسموحة بس لازم قيمتها string. مفيد للـ labels والـ metadata.
true / مش موجود: مفتوح
ده الـ default. مفيد لما الـ API لازم يتحمل حقول جديدة من غير ما يكسر، زي webhook من service تانية.
map كامل
object من غير properties و"additionalProperties": { "type": "integer" } ← ده Record<string, number>. مثلًا عدد كل صنف في المخزن.
- للـ input اللي بيتحفظ:
falseغالبًا هو الصح. - للـ responses اللي بتستقبلها من حد تاني: سيبها مفتوحة، عشان لما يضيف حقل ميكسرش عندك.
- فيه فخ كبير مع
allOf: هتشوفه في فصل ٥.
الـ enum والـ const
order-status.json
{ "type": "object", "properties": { "status": { "enum": ["new", "paid", "shipped", "cancelled"] }, "apiVersion": { "const": "v2" }, "priority": { "enum": [1, 2, 3, null] } }}- الـ
enum: القيمة لازم تساوي واحدة من القايمة بالظبط. الرسالةmust be equal to one of the allowed valuesوالقايمة فيparams.allowedValues. - ينفع أنواع مختلفة:
[1, 2, 3, null]مسموح. - المقارنة deep equality: ينفع تحط objects وarrays جوه الـ
enum. - الـ
const: قيمة واحدة بس. مفيد جدًا كـ “discriminator” في الـ unions (فصل ٥). الرسالةmust be equal to constant. - الـ case بيفرق:
"Paid"مش زي"paid".
الـ annotations: الوصف والتوثيق
annotated.json
{ "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "https://example.com/schemas/signup.json", "title": "Signup", "description": "البيانات المطلوبة لإنشاء حساب جديد", "$comment": "الـ comment ده للمطورين بس ومش بيظهر لليوزر", "type": "object", "properties": { "id": { "type": "string", "readOnly": true }, "password": { "type": "string", "minLength": 8, "writeOnly": true }, "plan": { "description": "خطة الاشتراك", "enum": ["free", "pro"], "default": "free", "examples": ["pro"] }, "referrer": { "type": "string", "deprecated": true } }, "required": ["password"]}- الـ
titleوالـdescription: للـ docs والـ editors (بتظهر في الـ hover في VS Code) وللـ LLMs كمان. - الـ
default: القيمة اللي المفروض تتفهم لو الحقل مش موجود. Ajv مش بيحطها إلا بـuseDefaults. - الـ
examples: array من أمثلة. في OpenAPI 3.0 كانexampleمفرد. - الـ
readOnly/writeOnly: الـ server بيرجّعه بس / الـ client بيبعته بس. معلومة للأدوات، ومش بيمنعوا حاجة في Ajv لوحده. - الـ
deprecated: بيعلّم الحقل إنه هيتشال. الـ editors بتشطب عليه. - الـ
$comment: ملاحظة للمطورين جوه الـ schema.
الـ $schema والـ $id
- الـ
$schema: أول سطر، بيحدد الـ draft. الـ validator بيعرف منه يفسّر الـ keywords إزاي. اكتبه دايمًا في الـ root. - الـ
$id: URI فريد للـ schema. مش لازم يبقى URL حقيقي بيفتح، هو مجرد اسم. بيستخدمه الـ$refوكمانajv.getSchema(id). - الـ
$idبيحدد الـ base: أي$refrelative جوه الـ schema بيتحسب بالنسبة له، زي"$ref": "money.json".
خلّصت الفصل لما
🧠 اختبر نفسك
١) schema فيها type: integer. القيمة 3.0 و"3"؟
3.0 valid لأنها رياضيًا integer. "3" invalid لأنها string، إلا لو شغّلت coerceTypes.٢) حقل name في required، واليوزر بعت name: null. إيه اللي هيحصل؟
الـ required هيعدّي لأن الـ key موجود، بس الـ type هيفشل لو
type: string من غير null.٣) ليه additionalProperties: false مهم في body بتاع update؟
عشان يمنع mass assignment، يعني حد يبعت حقول زي
isAdmin أو balance وتتحفظ على طول.٤) إزاي تعبّر عن Record؟
type: object من غير properties، ومعاها additionalProperties فيها type: boolean.