الفكرة: schema واحدة، استخدامات كتير
API validationOpenAPI docsTS typeseditor autocompleteconfig validationLLM structured outputstool callingMongoDB validatorformscontract testsKafka events
ده أكبر سبب تختار JSON Schema: بتكتب العقد مرة، وأدوات كتير بتفهمه من غير ما تترجمه.
OpenAPI
الـ OpenAPI بتوصف الـ API كله: الـ paths والـ methods والـ responses. وجوّاها، شكل الـ bodies مكتوب JSON Schema:
openapi.yaml
openapi: 3.1.0info: { title: Koshary API, version: 1.0.0 }servers: [{ url: "https://api.koshary.example" }]paths: /orders: post: operationId: createOrder summary: طلب جديد security: [] requestBody: required: true content: application/json: schema: { $ref: "#/components/schemas/CreateOrder" } responses: "201": { description: created } "400": { description: validation error }components: schemas: CreateOrder: type: object required: [customer, items] additionalProperties: false properties: customer: { type: string, minLength: 2 } notes: { type: [string, "null"], maxLength: 200 } items: type: array minItems: 1 items: { $ref: "#/components/schemas/Item" } Item: type: object required: [sku, qty] properties: sku: { enum: [koshary-small, koshary-large] } qty: { type: integer, minimum: 1, examples: [2] }| الحاجة | OpenAPI 3.0 | OpenAPI 3.1 |
|---|---|---|
| الـ JSON Schema | نسخة معدّلة من draft-04/05 | متوافقة مع 2020-12 بالكامل |
| يسمح بـ null | nullable: true | type: [string, "null"] |
| المثال | example (واحد) | examples (array)، وexample deprecated |
الـ exclusiveMinimum | boolean | رقم |
الـ const، if/then، prefixItems، $defs | مش موجودين | موجودين |
keywords جنب $ref | بتتجاهل | بتتطبق |
| تحدد الـ dialect | لأ | jsonSchemaDialect في الـ root |
- الملف ده عدّى من
redocly lint: بـ warning واحد بس إن مفيشlicense. - في 3.1 تقدر تاخد
order.jsonمن الـ Lab وتحطه زي ما هو تحتcomponents/schemas. - الـ
discriminatorوxml: keywords خاصة بـ OpenAPI، مش من JSON Schema. - الأدوات: Swagger UI وRedoc للـ docs، وgenerators للـ clients، وFastify بيطلّع OpenAPI من الـ route schemas بـ
@fastify/swagger. راجع الـ docs لدعم 3.1 في كل أداة. - لو الأداة لسه 3.0: Zod 4 بيطلّع
target: "openapi-3.0"(فصل ١٠).
Autocomplete للـ config files
الـ VS Code (وغيره) بيقرا JSON Schema ويديك autocomplete وhover docs وخط أحمر تحت الغلط وإنت بتكتب.
config.json
{ "$schema": "./config.schema.json", "port": 3000, "db": { "url": "postgres://localhost/app" }}- الـ
$schemaجوه ملف JSON: VS Code بيستخدمه على طول. ممكن يبقى path محلي أو URL. - خلي بالك: لو الـ schema فيها
additionalProperties: false، لازم تسمح بـ"$schema": { "type": "string" }في الـ properties، وإلا الحقل ده نفسه هيبقى غلط. - في YAML: بـ extension الـ YAML بتاع Red Hat، حط comment في أول الملف:
docker-compose.yml
# yaml-language-server: $schema=https://json.schemastore.org/docker-compose.jsonservices: web: image: nginx- من غير ما تكتب حاجة: موقع SchemaStore فيه schemas لمئات الملفات المشهورة (
package.json،tsconfig.json،.eslintrc، GitHub workflows،docker-compose…)، وVS Code بيطابقها بالاسم أوتوماتيك. عشان كده بتلاقي autocomplete فيtsconfig.jsonمن غير أي إعداد. - أو من الـ settings:
.vscode/settings.json
{ "json.schemas": [ { "fileMatch": ["config/*.json"], "url": "./schemas/config.schema.json" } ]}Validate الـ config وقت الـ startup
الـ autocomplete بيساعد وإنت بتكتب، بس السيرفر نفسه لازم يرفض config غلط قبل ما يبدأ:
load-config.js
import { readFileSync } from "node:fs";import Ajv from "ajv/dist/2020.js";import addFormats from "ajv-formats";const read = (f) => JSON.parse(readFileSync(new URL(f, import.meta.url), "utf8"));const ajv = new Ajv({ allErrors: true, useDefaults: true });addFormats(ajv);const validate = ajv.compile(read("./config.schema.json"));const config = read("./config.json");if (!validate(config)) { console.error("config.json فيه مشاكل:"); for (const e of validate.errors) console.error(" ", e.instancePath || "/", e.message, e.params.additionalProperty ?? ""); process.exit(1);}console.log("config تمام", config);مع config فيه "port": "3000" و"logLevel": "verbose" وpoolmax (typo):
output
config.json فيه مشاكل: /port must be integer /logLevel must be equal to one of the allowed values /db must NOT have additional properties poolmax- الـ typo اتلقط:
poolmaxبدلpoolMax. من غيرadditionalProperties: falseكان هيتجاهل بهدوء، والـ pool هيشتغل بالـ default وإنت فاكر إنه 20. - ولما الـ config صح:
useDefaultsحطlogLevel: 'info'وpoolMax: 10من الـ schema. - نفس الفكرة لـ env vars: حطهم في object وراجعهم بـ
coerceTypes: true.
الـ LLMs: structured outputs والـ tool calling
الـ LLM APIs بتستخدم JSON Schema في مكانين:
Structured outputs
بتبعت schema مع الـ request، والموديل مُجبر يرد بـ JSON مطابق ليها. في OpenAI ده response_format من نوع json_schema ومعاه strict: true. راجع الـ docs للشكل بالظبط.
Tool / function calling
كل tool ليها parameters (أو input_schema في Anthropic، أو inputSchema في MCP) مكتوبة JSON Schema. الموديل بيقرا الـ description بتاع كل حقل عشان يعرف يملاه إزاي.
review.schema.json
{ "type": "object", "properties": { "sentiment": { "type": "string", "enum": ["positive", "neutral", "negative"] }, "score": { "type": "integer", "minimum": 1, "maximum": 5, "description": "تقييم من 1 لـ 5" }, "dish": { "type": ["string", "null"], "description": "اسم الأكلة لو اتذكرت، وإلا null" } }, "required": ["sentiment", "score", "dish"], "additionalProperties": false}- الـ strict mode في OpenAI ليه قيود: كل الحقول لازم تبقى في
required، وadditionalProperties: falseإجباري في كل object، والـ root لازم object. الحقل “الاختياري” بيتعمل بـtype: [string, "null"]زيdishفوق. - وفيه keywords مش مدعومة أو ليها حدود: في الـ strict mode، والقايمة بتتغير. راجع الـ docs قبل ما تعتمد على
patternأوformatأوoneOf. - اكتب
descriptionكويس: دي الـ prompt بتاع كل حقل. - Zod أسهل هنا:
z.toJSONSchemaبيطلّعadditionalProperties: falseوrequired، والـ SDKs بتقبل Zod مباشرة. راجع الـ docs.
check-llm.js
import Ajv from "ajv/dist/2020.js";import { readFileSync } from "node:fs";const validate = new Ajv({ allErrors: true }).compile(JSON.parse(readFileSync("./review.schema.json", "utf8")));// ردود جاية من الموديل (نص)const replies = [ '{"sentiment":"positive","score":5,"dish":"كشري"}', '{"sentiment":"Positive","score":"5"}', 'أكيد! ده الـ JSON: {"sentiment":"positive"}',];for (const text of replies) { let data; try { data = JSON.parse(text); } catch { console.log("مش JSON أصلًا"); continue; } console.log(validate(data) ? "تمام" : validate.errors.map((e) => `${e.instancePath || "/"} ${e.message}`).join(" | "));}output
تمام/ must have required property 'dish' | /sentiment must be equal to one of the allowed values | /score must be integerمش JSON أصلًا- لو فشل: ابعت الـ errors للموديل تاني في رسالة (“صلّح دول”) وجرّب مرة أو اتنين، وبعدين fallback.
MongoDB: الـ validator جوه الداتابيز
mongosh
db.createCollection("orders", { validator: { $jsonSchema: { bsonType: "object", required: ["customer", "total", "status"], properties: { customer: { bsonType: "string", minLength: 2 }, total: { bsonType: "int", minimum: 0 }, status: { enum: ["new", "paid", "shipped"] }, }, }, }, validationLevel: "strict", // أو "moderate": سيب الـ documents القديمة الغلط validationAction: "error", // أو "warn": اكتب في الـ log بس});- الـ
$jsonSchema: مبني على draft-04، ومعاهbsonTypeعشان أنواع زيintوlongوdecimalوdateوobjectId. - مش كل الـ keywords موجودة: حسب الـ docs مفيش
$refولا$schemaولاdefaultولاdefinitionsولاformatولاid. راجع الـ docs للنسخة بتاعتك. - الفايدة: آخر خط دفاع. حتى لو script قديم أو حد من الـ shell كتب في الـ collection، الـ document الغلط بيترفض.
- الـ
moderate: بيطبق الـ validation على الـ inserts وعلى تعديل الـ documents اللي هي أصلًا valid بس. مفيد وإنت بتضيف validation على collection قديمة. - ملحوظة أمانة: المثال ده مكتوب من الـ docs ومتجرّبش هنا، لأن MongoDB 8 مرضيش يشتغل على الـ kernel بتاع جهاز التجربة.
حاجات تانية بتستخدمها
- الـ forms:
react-jsonschema-form(RJSF) وJSON Formsبيبنوا form كاملة من schema، ومعاهاuiSchemaللشكل. - الـ contract testing: في الـ tests، اعمل validate لكل response على الـ schema بتاعة الـ OpenAPI. لو حد غيّر الـ API وكسر العقد، الـ test يقع. وPostman فيه
pm.response.to.have.jsonSchema(schema). - الـ Kafka Schema Registry: (Confluent وغيره) بيدعم Avro وProtobuf وJSON Schema، وبيرفض schema جديدة لو بتكسر الـ compatibility (BACKWARD / FORWARD / FULL).
- الـ Kubernetes CRDs: بتوصف الـ custom resources بـ
openAPIV3Schema، وهي subset من JSON Schema. - الـ Helm: ملف
values.schema.jsonبيتراجع عليه الـvalues.yamlوقتhelm install. - الـ VS Code extensions: الـ settings اللي بتعرّفها في
contributes.configurationبتتكتب JSON Schema. - الـ docs generators: أدوات بتحوّل الـ schema لصفحة docs فيها كل حقل ووصفه.
خلّصت الفصل لما
🧠 اختبر نفسك
١) schema فيها nullable: true، شغالة في OpenAPI 3.0. نقلتها لـ 3.1، إيه اللي يتغير؟
في 3.1 مفيش
nullable، اكتب type array فيها "null". وكمان example يبقى examples.٢) ليه config.json فيه $schema ممكن يفشل في الـ validation بتاعتك؟
لأن الـ schema فيها
additionalProperties: false ومش معرّفة $schema كـ property. ضيفها كـ string.٣) في OpenAI strict mode، عايز حقل اختياري. تعمل إيه؟
حطه في required برضه، وخلي الـ type يقبل null، والموديل يرجّع null لو مفيش قيمة.
٤) ليه تحط $jsonSchema في MongoDB لو الـ API بيعمل validation أصلًا؟
آخر خط دفاع ضد أي حد بيكتب في الداتابيز من غير الـ API: scripts، migrations، shell، أو service تانية.