JSON Toolkit

النقاط الأساسية لتصميم عقود بيانات JSON API

الخلاصة: ستة أمور يصعب جداً تغييرها بعد النشر: أغلفة الاستجابة، صيغ الأخطاء، شكل الترقيم، تمثيل المعرفات والوقت، دلالات null مقابل الغياب، وتطور الإصدار.

متوسطقراءة نحو 13 دقيقةنُشر في 2026-10-30حُدِّث في 2026-10-30
غلاف النقاط الأساسية لتصميم عقود بيانات JSON API

الخلاصة: اقفل غلاف الاستجابة، وصيغة الأخطاء، وتمثيل المعرف والوقت، ودلالات null مقابل الغياب في العقد قبل مناقشة تفاصيل الترقيم والتسمية؛ وغلّف المستوى الأعلى دائماً في غلاف data حتى يمكن إضافة الترقيم ومعرفات التتبع لاحقاً دون كسر المستدعين الحاليين.

لا توجد إجابات قياسية في تصميم API، لكن توجد بعض القرارات "الصعب استرجاعها بعد الاختيار" التي يُستحسن التفكير فيها قبل كتابة أول سطر كود.

1. تغليف الاستجابة أم لا

ثلاثة أنماط:

// النمط A: استجابة عارية
{ "id": 1, "name": "张三" }

// النمط B: غلاف data
{ "data": { "id": 1, "name": "张三" } }

// النمط C: data + metadata
{ "data": { "id": 1, "name": "张三" }, "meta": { "requestId": "a1b2" } }
النمط الإيجابيات السلبيات
استجابة عارية الأبسط، طبقة أقل للواجهة لا يمكن إرفاق بيانات وصفية؛ المصفوفة في المستوى الأعلى تحمل مخاطر اختطاف JSON والتطور
غلاف data يسهل إضافة meta / errors / links مستوى تداخل إضافي، ويجب أن تكتب الواجهة res.data.data
data + metadata أفضل قابلية للملاحظة يتطلب اتفاقاً صارماً على الحقول التي تنتمي إلى meta

التوصية: استخدم غلاف data بشكل موحّد للقوائم والتفاصيل معاً، ولا تجعل المستوى الأعلى مصفوفة أبداً. بذلك لا تكسر إضافة الترقيم أو معرفات التتبع العقد لاحقاً.

2. صيغة الأخطاء: انسخ RFC 9457

اختراع بنية أخطاء خاصة بك مصدر ضخم لتكلفة التعاون. يحدد RFC 9457 (الذي يحل محل RFC 7807) نوع application/problem+json:

{
  "type": "https://bbdrug.com/problems/insufficient-balance",
  "title": "余额不足",
  "status": 422,
  "detail": "账户余额 12.00 元,本次扣款需要 30.00 元",
  "instance": "/accounts/A1001/payments/p-889",
  "balance": "12.00",
  "required": "30.00"
}
الحقل المعنى مطلوب
type URI ثابت لنوع المشكلة مستحسن
title ملخص قصير قابل للقراءة البشرية مستحسن
status رمز حالة HTTP مستحسن
detail شرح محدد لما حدث هذه المرة اختياري
instance نسخة المورد التي واجهت المشكلة اختياري
حقول الامتداد حقول أعمال اعتباطية اختياري

حقول الامتداد (مثل balance / required أعلاه) تسمح بها المواصفات، ويمكن للواجهة استخدامها لرسائل دقيقة بدلاً من تحليل نص detail.

3. الترقيم: المؤشر أفضل من الإزاحة

// ترقيم بالإزاحة
{ "data": [], "page": { "offset": 40, "limit": 20, "total": 137 } }

// ترقيم بالمؤشر
{ "data": [], "page": { "nextCursor": "eyJpZCI6MTAwfQ", "hasMore": true } }
البُعد ترقيم بالإزاحة ترقيم بالمؤشر
صعوبة التنفيذ منخفضة (LIMIT/OFFSET) متوسطة (يتطلب مفتاح فرز مستقر)
الأداء في الصفحات العميقة ضعيف (يزداد الإزاحة سوءاً) جيد
عند تغيّر البيانات تكرارات أو عناصر مفقودة مستقر
الانتقال إلى صفحة ممكن غير ممكن
يحتاج إلى عدد إجمالي يتطلب عدّاً إضافياً لا يحتاج

التوصية: استخدم ترقيم الإزاحة لجداول الإدارة (التي تحتاج قفزات صفحات ومجاميع)، وترقيم المؤشر لخلاصات المستخدمين اللانهائية.

4. المعرفات والوقت

4.1 استخدم النصوص دائماً للمعرفات

جاء المنطق في مقالة الأنواع: على جانب JavaScript، تفقد القيم فوق 2^53 دقتها بصمت. وحتى لو كان المعرف الحالي عدداً صحيحاً صغيراً يتزايد تلقائياً، فإن استخدام النصوص يترك مجالاً للتحول لاحقاً إلى معرفات snowflake.

{ "id": "1000000000000000001" }

4.2 استخدم نصوص RFC 3339 للوقت

{
  "createdAt": "2026-10-30T10:41:45+08:00",
  "updatedAt": "2026-10-30T02:41:45Z"
}
  • لا تستخدم طوابع زمنية رقمية بالميلي ثانية: فهي صعبة القراءة، والوحدة (ثوانٍ/ميلي ثانية) تُخطأ كثيراً؛
  • حدد المنطقة الزمنية صراحةً: إما بـZ أو بإزاحة؛ ولا تستخدم أبداً "وقت محلي بلا منطقة زمنية"؛
  • سمّ الحقول بشكل موحّد xxxAt حتى يتضح من النظرة الأولى أنه نقطة زمنية.

5. null والغياب

يجب أن تختار واحدة وتكتبها في الوثائق:

النهج التعبير عن "لا قيمة" التعبير عن "لا تعدّل"
النهج A قيمة الحقل null احذف الحقل من الطلب
النهج B الحقل غير موجود احذف الحقل من الطلب

مشكلة النهج A أن "محذوف" و"مرسل كـnull" يعنيان شيئين مختلفين في PATCH، وهو أمر يسهل إساءة تفسيره؛ ومشكلة النهج B أن JSON.stringify في الواجهة يُسقط الحقول التي قيمتها undefined، وهو ما يصادف تطابق الدلالات.

التوصية العملية:

  • استخدم النهج A لأجسام الاستجابة (null يعني بوضوح لا قيمة، والواجهة لا تحتاج إلى تمييز undefined)؛
  • استخدم النهج B لأجسام الطلب (خصوصاً PATCH)، مع Merge Patch أو JSON Patch لجعل الدلالات صريحة.

6. تسمية الحقول والبنية

القرار الخيارات الشائعة تحفظ
أسلوب التسمية camelCase (نظام JS) / snake_case (Python، قواعد البيانات) موحّد في المشروع، لا تخلط
قيم enum أحرف صغيرة بشرطات سفلية، "pending_review" لا تستخدم أرقاماً؛ فهي صعبة القراءة
الحقول المنطقية isActive / enabled لا تستخدم status: 0/1
حقول المصفوفات أسماء جمع، items أعِد [] للمصفوفات الفارغة، لا null أبداً
عمق التداخل لا يزيد على 3 مستويات أعمق من ذلك، فقسّمه أو اعرض معامل expand

إعادة [] بدلاً من null للمصفوفات الفارغة مهمة جداً: فلن يتعطل data.items.map(...) في الواجهة بسبب null.

7. استراتيجية التطور

التغيير يكسر التوافق كيفية التعامل
إضافة حقل اختياري لا انشره مباشرة
إضافة حقل مطلوب نعم أضف قيمة افتراضية، أو أطلقه بإصدار
إزالة حقل نعم علّمه كمهجور أولاً، راقب المستدعين، ثم أزله
تغيير نوع حقل نعم أضف اسم حقل جديداً واكتب dual-write أثناء الانتقال
تغيير مجموعة قيم enum يعتمد يجب أن يكون لدى العميل بديل للقيم غير المعروفة
تغيير دلالات الحقل نعم عاملها كحقل جديد

ثلاث قواعد انضباط:

  1. يجب أن يكون العملاء متسامحين مع الحقول غير المعروفة (لا تحصر نفسك بـadditionalProperties: false)؛
  2. يجب أن يكون لدى العملاء فرع بديل لقيم enum غير المعروفة؛
  3. احتفظ بالحقول المهجورة لمدة إصدار رئيسي واحد على الأقل، وعلّمها بـDeprecation في ترويسات الاستجابة أو الوثائق.

8. مثال كامل

{
  "data": [
    {
      "id": "1001",
      "name": "JSON 权威指南",
      "price": "89.50",
      "tags": ["json", "spec"],
      "publishedAt": "2026-09-01T00:00:00+08:00",
      "author": null
    }
  ],
  "page": { "nextCursor": null, "hasMore": false },
  "meta": { "requestId": "req-7f3a", "tookMs": 12 }
}

تعكس هذه الاستجابة كل الاتفاقيات أعلاه: غلاف data، معرفات نصية، مبالغ كنصوص، طوابع زمنية بمنطقة زمنية، null بمعنى مفقود، مصفوفة فارغة، ترقيم بالمؤشر، وmeta لمعلومات القابلية للملاحظة.

9. قائمة الفحص

  • [ ] المستوى الأعلى ليس مصفوفة؛ استخدم غلاف data بشكل موحّد؛
  • [ ] استجابات الأخطاء تتبع RFC 9457 وتشمل type ثابتاً؛
  • [ ] شكل الترقيم يطابق سيناريو الأعمال؛
  • [ ] المعرفات نصوص، والوقت يستخدم RFC 3339؛
  • [ ] دلالات null والغياب مكتوبة في الوثائق؛
  • [ ] أسلوب التسمية موحّد في المشروع، وenums تستخدم نصوصاً صغيرة؛
  • [ ] العميل لديه بدائل للحقول غير المعروفة وقيم enum غير المعروفة؛
  • [ ] التغييرات الكاسرة تمر عبر إصدارات أو انتقال dual-write.

FAQ

كيف أصمم JSON API قابلة للتطور؟

غلّف القوائم والتفاصيل بشكل موحّد في غلاف data ولا تجعل المستوى الأعلى مصفوفة أبداً، حتى يمكن إضافة الترقيم ومعرفات التتبع دون كسر العقد. استخدم معرفات نصية وطوابع زمنية RFC 3339، واكتب دلالات null مقابل الغياب في الوثائق. أبقِ الحقول الجديدة اختيارية ومستقلة دلالياً، وامنح العملاء بدائل للحقول غير المعروفة وقيم enum.

لماذا لا أبتكر صيغة أخطاء مخصصة؟

اختراع بنية أخطاء خاصة بك مصدر ضخم لتكلفة التعاون. انسخ application/problem+json من RFC 9457، وحدد المشكلات بـtype ثابت، واسمح بحقول امتداد مثل balance وrequired، حتى تبني الواجهة رسائل دقيقة دون تحليل نص detail.

هل يجب أن يستخدم كل endpoint ترقيم المؤشر؟

لا. جداول الإدارة تحتاج قفزات صفحات ومجاميع، لذا يناسبها ترقيم الإزاحة أكثر؛ وخلاصات المستخدمين اللانهائية يجب أن تستخدم ترقيم المؤشر، الذي يبقى مستقراً في الصفحات العميقة ويتجنب التكرارات أو الفجوات عند تغيّر البيانات. اختر بناءً على ما إذا كان السيناريو يحتاج قفزات صفحات ومجاميع.

المراجع

الأدوات المطابقة

مقالات ذات صلة

JSON Pointer وPatch وMerge Patch

ثلاث مواصفات قصيرة تشكّل بروتوكول تغيير: Pointer يحدّد الموقع، وPatch يعدّل بدقة، وMerge Patch يدمج جزئيًا. والاختيار الخاطئ يضر بالمصفوفات وبالتزامن.

متوسطقراءة نحو 12 دقيقةحُدِّث في 2026-10-30

إدارة JSON للإعدادات وfixtures والبيانات المحلية

يجب أن تكون الإعدادات متعددة الطبقات ومتحققة؛ ويجب أن تكون fixtures حتمية وقابلة للقراءة والـdiff. إذا أُدير هذان النوعان من الملفات بشكل سيئ، تبقى تكاليف التشخيص مرتفعة لوقت طويل.

مبتدئقراءة نحو 10 دقيقةحُدِّث في 2026-10-30