معظم أخطاء واجهات برمجة التطبيقات (API) ليست أخطاء برمجية، بل أخطاء في العقد. الواجهة الأمامية تتوقع userId، والواجهة الخلفية ترسل user_id، ولا يكتشف أحد ذلك حتى QA. تطوير الـ API المعتمد على المواصفات أولًا يحل هذا بتعريف العقد قبل كتابة كود الخادم.
في هذا الدليل ستكتب مواصفات OpenAPI صغيرة يدويًا، ثم تستخدم الملف نفسه لإنشاء mock server، واختبارات عقد، ووثائق API قبل تنفيذ أي endpoint. قد تسمع هذا الأسلوب بأسماء مثل: spec-first، design-first، أو contract-first. الفكرة واحدة: اتفق على الواجهة أولًا، ثم ابنِ عليها.
ما هو تطوير الـ API المعتمد على المواصفات أولًا؟
تطوير واجهات برمجة التطبيقات بالمواصفات أولًا يعني كتابة عقد قابل للقراءة آليًا، غالبًا باستخدام OpenAPI، قبل تنفيذ الـ endpoint.
هذا العقد يحدد:
- المسارات مثل
/users - معاملات الاستعلام والمسار
- جسم الطلب
requestBody - شكل الاستجابة
- أكواد الحالة
- المخططات المشتركة
schemas
بدل أن تكون الوثائق نتيجة متأخرة للكود، تصبح المواصفات هي مصدر الحقيقة. الواجهة الأمامية تبني على mock generated من المواصفات، وQA يكتب اختبارات العقد منها، والواجهة الخلفية تنفذ بناءً عليها.
النتيجة العملية: مشاكل التكامل تظهر مبكرًا، قبل أن تصبح مكلفة.
دورة حياة spec-first مقابل code-first
ينتج النهجان في النهاية endpoints مشابهة، لكن الاختلاف في متى تظهر المشاكل ومن يستطيع البدء مبكرًا.
في code-first، غالبًا تكتب المعالجات أولًا ثم توثقها لاحقًا. في spec-first، تكتب العقد أولًا، ثم يعمل كل فريق بالتوازي بناءً على التعريف نفسه.
مثال عملي: إنشاء OpenAPI لـ /users
سنصمم endpoint بسيطة للمستخدمين. الهدف ليس بناء API كاملة، بل رؤية كيف يتحول ملف OpenAPI واحد إلى عقد قابل للاستخدام.
1. ابدأ برأس ملف OpenAPI
openapi: 3.0.3
info:
title: Users API
version: 1.0.0
servers:
- url: https://api.example.com/v1
هذا الجزء يحدد إصدار OpenAPI واسم الـ API ورابط الخادم الأساسي.
2. عرّف مخطط User
ضع الأشكال المشتركة داخل components/schemas حتى تعيد استخدامها لاحقًا باستخدام $ref.
components:
schemas:
User:
type: object
required:
- id
- email
- createdAt
properties:
id:
type: string
format: uuid
email:
type: string
format: email
name:
type: string
createdAt:
type: string
format: date-time
هذا المخطط يقول إن كائن User يجب أن يحتوي على:
idemailcreatedAt
كما يحدد صيغًا مهمة مثل uuid وemail وdate-time.
3. أضف GET /users
paths:
/users:
get:
summary: List users
operationId: listUsers
parameters:
- name: limit
in: query
schema:
type: integer
default: 20
maximum: 100
responses:
"200":
description: A list of users
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/User"
هنا عرّفنا endpoint ترجع قائمة مستخدمين، مع query parameter اسمه limit وحده الأقصى 100.
4. أضف POST /users
paths:
/users:
post:
summary: Create a user
operationId: createUser
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- email
properties:
email:
type: string
format: email
name:
type: string
responses:
"201":
description: User created
content:
application/json:
schema:
$ref: "#/components/schemas/User"
"400":
description: Invalid request body
هذه العملية تحدد أن إنشاء المستخدم يتطلب email، ويمكن أن يحتوي اختياريًا على name. عند النجاح ترجع 201 وكائن User. عند الخطأ ترجع 400.
5. الملف الكامل
استخدم هذا الملف كنقطة بداية قابلة للتشغيل:
openapi: 3.0.3
info:
title: Users API
version: 1.0.0
servers:
- url: https://api.example.com/v1
paths:
/users:
get:
summary: List users
operationId: listUsers
parameters:
- name: limit
in: query
schema:
type: integer
default: 20
maximum: 100
responses:
"200":
description: A list of users
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/User"
post:
summary: Create a user
operationId: createUser
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- email
properties:
email:
type: string
format: email
name:
type: string
responses:
"201":
description: User created
content:
application/json:
schema:
$ref: "#/components/schemas/User"
"400":
description: Invalid request body
components:
schemas:
User:
type: object
required:
- id
- email
- createdAt
properties:
id:
type: string
format: uuid
email:
type: string
format: email
name:
type: string
createdAt:
type: string
format: date-time
بهذا أصبح لديك عقد يحدد الحقول، الأنواع، القيود، وأكواد الحالة قبل كتابة أي كود backend.
ماذا تفعل بهذا الملف؟
السبب العملي لكتابة المواصفات أولًا هو أن ملفًا واحدًا يمكنه تشغيل ثلاث عمليات مهمة: النماذج، الاختبارات، والوثائق.
1. إنشاء Mock Server
خادم الـ mock يقرأ مواصفات OpenAPI ويعيد استجابات مطابقة للمخططات.
مثال: الواجهة الأمامية يمكنها استدعاء:
GET /users?limit=20
وتحصل على استجابة مثل:
[
{
"id": "4c84f9a2-8c9c-4e6e-b1c3-123456789abc",
"email": "user@example.com",
"name": "Ahmed",
"createdAt": "2026-06-02T10:00:00Z"
}
]
هذا يسمح لفريق frontend بالبدء قبل توفر التنفيذ الحقيقي.
2. كتابة اختبارات عقد Contract Tests
بما أن المواصفات تحدد أنواع الحقول والاستجابات، يمكنك استخدامها للتحقق من أن التنفيذ الحقيقي لا يخرج عن العقد.
أمثلة على ما يجب اختباره:
-
POST /usersيرجع201عند إرسالemailصالح. - الاستجابة تطابق مخطط
User. - الطلب بدون
emailيرجع400. -
GET /usersيرجع array من كائناتUser. -
limitلا يتجاوز100.
الفكرة ليست اختراع assertions جديدة، بل التحقق من أن الكود يطابق ما اتفق عليه الفريق.
3. توليد الوثائق
وثائق الـ API يمكن عرضها مباشرة من ملف OpenAPI. كل endpoint وparameter وschema يظهر من المصدر نفسه، لذلك لا تحتاج إلى نسخة وثائق منفصلة يمكن أن تصبح قديمة.
هذا يتكامل جيدًا مع سير عمل واجهة برمجة التطبيقات الأصلي لـ Git: المواصفات ملف نصي، وكل تغيير يظهر كـ diff يمكن مراجعته في pull request.
تنفيذ ذلك في Apidog
يدعم Apidog هذا الأسلوب من خلال وضع المواصفات أولًا Spec-First Mode. بدل التعامل مع OpenAPI كتصدير نهائي، يتعامل معه كمصدر المشروع.
سير العمل العملي يكون كالتالي:
- اكتب أو الصق مواصفات
/usersفي محرر OpenAPI. - يحلل Apidog الملف وينشئ endpoints بناءً عليه.
- يتم إنشاء mock server للعمليات الموجودة في المواصفات.
- تبدأ الواجهة الأمامية باستخدام الـ mock.
- يتم توليد الوثائق من المواصفات نفسها.
- عند جاهزية backend، شغّل العمليات كحالات اختبار.
- تحقق من أن الاستجابات الحقيقية تطابق schemas المعلنة.
الميزة المهمة هنا هي مزامنة Git ثنائية الاتجاه. تعيش المواصفات في المستودع، وتتم مراجعة التغييرات كـ commits. إذا عدّلت YAML في محررك ودفعت التغيير، يستطيع Apidog مزامنته. وإذا عدّلت داخل Apidog، يمكن أن تهبط التغييرات كتثبيت يمكن للفريق مراجعته.
للمقارنة الأعمق بين هذا النهج ونهج التصميم أولًا، راجع: المواصفات أولًا مقابل التصميم أولًا في Apidog.
قائمة مراجعة قبل اعتماد المواصفات
قبل أن يبدأ الفريق بالبناء على ملف OpenAPI، تحقق من التالي:
- ملف OpenAPI صالح ولا يحتوي على أخطاء schema.
- كل endpoint يحتوي على response نجاح واحد على الأقل.
- كل endpoint يحتوي على response خطأ واحد على الأقل.
- الأشكال المشتركة موجودة في
components/schemas. - يتم استخدام
$refبدل نسخ schemas في أكثر من مكان. - الحقول المطلوبة محددة بـ
required. - الصيغ مثل
uuidوemailوdate-timeمضبوطة عند الحاجة. - معاملات الاستعلام لها أنواع وحدود واضحة.
- ملف المواصفات محفوظ في Git.
- التغييرات على العقد تتم مراجعتها عبر pull requests.
- mock server يعمل من نفس المواصفات.
- اختبارات العقد تتحقق من الاستجابات الحقيقية.
- الوثائق المنشورة مولدة من الملف نفسه.
إذا تحققت هذه النقاط، يمكن للفرق العمل بالتوازي بناءً على عقد واحد بدل الاعتماد على افتراضات مختلفة.
الأسئلة الشائعة
هل spec-first هو نفسه design-first؟
غالبًا نعم. مصطلحات spec-first وdesign-first وcontract-first تشير إلى المبدأ نفسه: تعريف الواجهة قبل التنفيذ. الاختلاف أن spec-first يركز على ملف المواصفات نفسه، مثل OpenAPI، كمخرج عملي قابل للاستخدام.
هل يجب كتابة YAML يدويًا؟
لا. يمكنك استخدام محرر مرئي لإنتاج OpenAPI، أو كتابة YAML مباشرة. المهم أن يكون العقد موجودًا ومتفقًا عليه قبل تنفيذ الكود.
في فرق كثيرة، يبدأ التصميم بصريًا ثم تتم مراجعة YAML في Git قبل التنفيذ.
كيف أمنع تباعد المواصفات عن الكود؟
اجعل المواصفات مصدر الحقيقة، ثم افرضها آليًا:
- خزّن ملف OpenAPI في Git.
- راجع أي تغيير في العقد عبر pull request.
- شغّل contract tests في CI.
- افشل build إذا لم تطابق الاستجابة المخطط.
- استخدم مزامنة ثنائية الاتجاه إذا كان الفريق يحرر المواصفات من أكثر من مكان.
الخلاصة
تطوير الـ API بالمواصفات أولًا هو تغيير بسيط في الترتيب: اكتب العقد، راجعه، ثم نفّذ الكود. لكنه يقلل أخطاء التكامل، ويسمح للواجهة الأمامية وQA والواجهة الخلفية بالعمل بالتوازي.
إذا أردت تجربة الدورة كاملة، افتح وضع المواصفات أولًا Spec-First Mode في Apidog واربطه بمستودعك.


Top comments (0)