لماذا أكتب واجهة الـ API قبل قاعدة البيانات
البدء بالجداول يبدو منطقيًا، ثم تجد أن شكل قاعدة البيانات تسرّب إلى كل شاشة في التطبيق. هذا ترتيب معاكس، وسبب اتّباعه.

الإجابة المختصرة
ابدأ بالـ API لأنه العقد الذي يراه كل من يبني على نظامك، بينما الجداول تفصيل داخلي يمكن تغييره لاحقًا. حين تبدأ بالجداول ينتهي شكلها إلى الواجهة، وتصبح كل هجرة في قاعدة البيانات تغييرًا كاسرًا للعملاء.
في هذه الصفحة
- العقد يبقى، والتفصيل يتغيّر
- ابدأ من الشاشة، لا من الجدول
- ثلاث قواعد أتبعها في شكل الاستجابة
- اكتب العقد قبل الكود
- متى لا ينفع هذا الترتيب؟
- ما معنى تصميم API أولًا؟
- ما الذي يصعب تغييره فعلًا؟
- كيف تبدأ عمليًا؟
- ما الذي يستحق الالتزام به من البداية
- ما هو الـ API في هذا السياق؟
- ثلاثة أخطاء شائعة
- كيف تعرف أن العقد جيد؟
- نسخ العقد لا نسخ الشيفرة
- خلاصة
- أدوات تساعد
- ماذا عن الأداء؟
- قائمة مراجعة قبل نشر عقد جديد
الترتيب المعتاد: نرسم الجداول، ثم نبني فوقها نقاط النهاية، ثم تستهلكها الواجهة. الترتيب الذي أتّبعه منذ سنوات معاكس، وسببه واحد: الـ API عقد، والجداول تفصيل.
العقد يبقى، والتفصيل يتغيّر
قاعدة البيانات مِلكك وحدك. يمكنك تقسيم جدول، أو إضافة فهرس، أو نقل عمود إلى جدول آخر، دون أن يلاحظ أحد. الـ API ليس كذلك: كل حقل تنشره يصبح شيئًا يعتمد عليه شخص ما — تطبيق الجوال، لوحة الإدارة، تكامل خارجي — ولا يمكن سحبه بسهولة.
لهذا يكون السؤال الأول: ما الذي يحتاجه من يستهلك هذه الواجهة، بالشكل الذي يحتاجه؟ لا: ما الذي أخزّنه؟
ابدأ من الشاشة، لا من الجدول
الطريقة العملية: خذ الشاشة الأهم في المنتج واكتب الاستجابة التي تجعل بناءها طلبًا واحدًا لا خمسة.
GET /api/v1/orders/1042
{
"id": "1042",
"status": "shipped",
"placedAt": "2026-08-20T10:14:00Z",
"customer": { "id": "c-88", "name": "سارة" },
"items": [{ "sku": "TB-12", "title": "لوحة مفاتيح", "qty": 1 }],
"total": { "amount": 1490, "currency": "EGP" }
}الآن اسأل: كم جدولًا يلزم لتوليد هذا؟ ثلاثة أو أربعة. ولاحظ أن ترتيبها الداخلي لم يعد مهمًا — ما دام هذا الشكل ثابتًا، أستطيع تغيير التخزين تحته متى شئت.
لو بدأت من الجداول لكانت النتيجة على الأرجح ثلاث نقاط نهاية تعكس الجداول حرفيًا، وواجهة تجمعها بنفسها في كل شاشة، وثلاثة أماكن تتكرّر فيها القاعدة نفسها.
ثلاث قواعد أتبعها في شكل الاستجابة
- معرّفات نصّية (string) دائمًا. حتى لو كان العمود رقمًا في قاعدة البيانات. الرقم يغري من يستهلك الواجهة بالحساب عليه وبالترتيب حسبه، والانتقال إلى معرّف آخر لاحقًا يصبح كسرًا.
- المال كائن، لا رقم عشري.
{ amount, currency }، والمبلغ بأصغر وحدة. العملة الضمنية تعمل حتى أول عميل خارج بلدك. - التاريخ ISO 8601 بتوقيت UTC، والسلسلة الفارغة تعني "لا يوجد".
nullتعمل أيضًا، لكن المهم أن تختار واحدًا وتلتزم به في كل الحقول.
شكل قاعدة البيانات الذي يتسرّب إلى الـ API يتحوّل بعد سنة إلى قيد على قاعدة البيانات نفسها: لن تستطيع تغييرها لأن أحدًا يقرأها من الخارج.
اكتب العقد قبل الكود
قبل كتابة أي معالج، أكتب الأنواع أولًا — واجهات TypeScript أو ملف OpenAPI، لا فرق كبير. الفائدة ليست التوثيق بل أن مراجعة العقد أرخص ألف مرة من مراجعة تنفيذه. الخطأ في اسم حقل يُصلح في ثانية على الورق، وفي أسبوع بعد أن بُني عليه تطبيقان.
بعدها تأتي الجداول، وسؤالها الوحيد: كيف أخدم هذا العقد بأقل عدد من الاستعلامات؟ وهذا سؤال أداء، وهو نوع المشاكل التي يسهل حلّها لاحقًا — بعكس تغيير عقد منشور.
متى لا ينفع هذا الترتيب؟
حين يكون النظام تحليليًا في جوهره: مستودع بيانات، أو تقارير فوق بيانات موجودة أصلًا. هناك الشكل الطبيعي هو شكل البيانات نفسه، والـ API لاحق له لا سابق عليه. القاعدة تخصّ المنتجات التي تُبنى فوقها واجهات، وهي الأغلب.
ما معنى تصميم API أولًا؟
تصميم API يعني كتابة العقد الذي يراه المستهلك — المسارات، والحقول، ورموز الأخطاء — قبل أن تقرّر كيف تُخزَّن البيانات. الفكرة ليست تجاهل قاعدة البيانات بل ترتيب القرارين حسب تكلفة التراجع عنهما.
الجدول يمكن تغييره بترحيل في ليلة واحدة. العقد المنشور موجود في تطبيقات لا تملكها، وفي شيفرة كتبها غيرك، وفي تكاملات نسيها أصحابها.
ما الذي يصعب تغييره فعلًا؟
| القرار | تكلفة التغيير | من يتأثّر |
|---|---|---|
| اسم عمود في جدول | ترحيل واحد | فريقك وحده |
| فهرس أو نوع بيانات | ترحيل مدروس | فريقك وحده |
| اسم حقل في الاستجابة | إصدار جديد كامل | كل مستهلك |
| شكل رمز الخطأ | كسر صامت عند المستهلك | كل مستهلك |
| ترقيم الصفحات | إعادة كتابة عند الطرفين | كل مستهلك |
| مصادقة أو صلاحيات | نافذة انتقال طويلة | كل مستهلك |
الصفوف الثلاثة الأولى داخلية، والباقي عام. وحين تبدأ من الجدول ينتهي بك الأمر إلى تسريب الصفوف الداخلية إلى الصفوف العامة.
كيف تبدأ عمليًا؟
- اكتب مثالًا لطلب واستجابة لكل عملية، بالقيم الحقيقية لا بالأنواع.
- سمِّ الأخطاء قبل المسارات: ما الذي يمكن أن يفشل، وماذا يقرأ المستهلك حينها.
- قرّر الترقيم والفرز مرة واحدة لكل القوائم، لا لكل مسار على حدة.
- راجعه مع مستهلك حقيقي — واجهة أمامية أو فريق آخر — قبل أن تكتب شيفرة.
- ثم صمّم الجداول لتخدم هذا العقد.
الجدول تفصيل داخلي، والعقد وعد. غيّر التفصيل متى شئت، وفكّر مرّتين قبل أن تعد.
ما الذي يستحق الالتزام به من البداية
- رموز الحالة القياسية كما تعرّفها مواصفة HTTP، لا رموزًا خاصة بك.
- شكل خطأ واحد لكل الاستجابات: رمز، ورسالة قابلة للقراءة، ومعرّف للتتبّع.
- أسماء حقول ثابتة الاصطلاح، فاختلاف
user_idوuserIdفي المنتج نفسه يكلّف كل مستهلك سطرًا إضافيًا. - ترقيم بالمؤشّر لا بالإزاحة في أي قائمة قد تكبر.
للتوسّع: تطوير الويب، وأدوات المطورين، وبناء المنتجات.
ما هو الـ API في هذا السياق؟
API هو العقد بينك وبين من يستهلك خدمتك: ما الذي يُطلَب، وما الذي يعود، وماذا يحدث حين يفشل شيء. ليس مجرّد مسارات، بل وعد بأن هذه الأشكال ستبقى كما هي أو تتغيّر بطريقة معلنة.
وهذا الوعد هو ما يجعل ترتيب القرارات مهمًّا. من يبدأ بالجداول يصمّم API يعكس تخزينه، ومن يبدأ بالعقد يصمّم تخزينًا يخدم استخدامًا حقيقيًا.
ثلاثة أخطاء شائعة
- تسريب أسماء الجداول إلى أسماء الحقول، فيصير كل تغيير داخلي تغييرًا عامًّا.
- إرجاع كل شيء لأن الاستعلام أرجعه، بدل إرجاع ما تحتاجه الواجهة فعلًا.
- أخطاء غير موصوفة: رمز 500 لكل شيء، فلا يعرف المستهلك هل يعيد المحاولة أم يصحّح طلبه.
كيف تعرف أن العقد جيد؟
اكتب مستهلكًا وهميًا قبل الخادم: شيفرة قصيرة تستدعي الـ API كما ستستدعيه الواجهة. إن كانت مربكة عند الكتابة فستكون مربكة للجميع، والتصحيح الآن يكلّف دقائق بدل إصدار جديد.
والسؤال الأخير قبل النشر: ماذا سيحدث حين نضيف حقلًا؟ إن كانت الإجابة "يكسر المستهلكين" فالعقد ليس جاهزًا بعد. الإضافة يجب أن تكون آمنة دائمًا، والحذف وحده هو ما يستحق إصدارًا جديدًا.
نسخ العقد لا نسخ الشيفرة
حين يتغيّر العقد فعلًا، النسخة الجديدة تعيش إلى جانب القديمة لا مكانها. أعلن تاريخ إيقاف، وأرسله إلى من يستهلك فعلًا لا إلى قائمة بريدية، وراقب حركة النسخة القديمة حتى تصل إلى الصفر.
الفرق بين إصدارين متعايشين وكسر مفاجئ هو أسابيع من العمل عندك مقابل أسابيع من العمل عند كل مستهلك. والحساب يميل دائمًا إلى الطرف الأول، لأن عددهم أكبر من عددك.
خلاصة
ابدأ من المثال الحقيقي للطلب والاستجابة، سمِّ الأخطاء قبل المسارات، راجعه مع مستهلك حقيقي، ثم صمّم الجداول. الترتيب وحده يوفّر أغلب ما يُنفَق لاحقًا على إصدارات لم تكن ضرورية.
أدوات تساعد
مواصفة مكتوبة — OpenAPI أو ما يشبهها — تفيد لسببين لا علاقة لهما بالتوثيق: تجبرك على تسمية الأخطاء وأشكال الاستجابة قبل الكتابة، وتتيح توليد عميل وهمي يستخدمه فريق الواجهة قبل أن يجهز الخادم.
ذلك يقصّر دورة المراجعة من أسابيع إلى ساعة، وهو أهمّ ما يجنيه المشروع الصغير من تصميم الـ API أولًا. العقد أولًا، والجداول بعده. هذا الترتيب لا يكلّف شيئًا في اليوم الأول ويوفّر إصدارًا كاملًا في الشهر السادس، وهو أرخص قرار معماري متاح في أي خدمة جديدة.
ماذا عن الأداء؟
قرار العقد لا يقيّد الأداء بقدر ما يبدو. يمكن لاستجابة واحدة أن تُجمَّع من ثلاثة استعلامات، ويمكن لثلاثة مسارات أن تخدمها جداول مشتركة. ما يقيّد الأداء هو تسريب شكل التخزين إلى العقد، لأنك بعدها لا تستطيع تغيير التخزين دون كسر المستهلكين.
ابدأ من العقد، ثم حسّن التخزين خلفه بحرّية. هذا هو الترتيب الذي يبقي الخيارين مفتوحين معًا.
قائمة مراجعة قبل نشر عقد جديد
- أمثلة حقيقية لكل عملية: طلب واستجابة بقيم لا بأنواع.
- شكل خطأ واحد لكل الاستجابات، مع معرّف تتبّع.
- ترقيم بالمؤشّر في كل قائمة قد تكبر.
- إضافة الحقول آمنة، والحذف وحده يستحقّ إصدارًا.
- مستهلك وهمي كتبه شخص غيرك وجرّبه قبل النشر.
خمس نقاط، ونصف يوم عمل، مقابل إصدار كامل لا تضطر إليه لاحقًا.
أسئلة شائعة
- أليس هذا يعني كتابة كود تحويل إضافي بين الجداول والاستجابة؟
- نعم، وهو كود مقصود. طبقة التحويل هي المكان الذي يُحفظ فيه العقد ثابتًا بينما يتغيّر التخزين تحته. الكلفة الحقيقية ليست في كتابتها بل في غيابها، حين تصبح كل هجرة قاعدة بيانات تغييرًا كاسرًا.
- هل أستخدم GraphQL بدلًا من ذلك؟
- GraphQL ينقل مسؤولية تحديد الشكل إلى المستهلك، ولا يلغي الحاجة إلى تصميم واعٍ للأنواع والحدود. المبدأ نفسه ينطبق: صمّم ما تنشره أولًا، ثم قرّر كيف تخزّنه.
- كيف أغيّر عقدًا منشورًا إن اضطررت؟
- بإضافة لا بتعديل: أضف الحقل الجديد بجانب القديم، حوّل المستهلكين تدريجيًا، ثم احذف القديم بعد مهلة معلنة. الحذف المباشر هو ما يكسر، لا الإضافة.
المصادر
- HTTP semantics — MDN — MDN Web Docs
- OpenAPI Specification — OpenAPI Initiative
- HTTP Semantics — IETF RFC 9110
نشرته
MrBebo
محتوى تقني عربي يقدمه بهاء طه حول البرمجة، الذكاء الاصطناعي، Web2 وWeb3، تطوير التطبيقات، بناء المنتجات وأدوات المطورين.
عن المدوّنة