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

الطبقات الثلاث وأين يقع خطأك

  1. طبقة الشبكة: لم يصل الطلب أصلاً أو انقطع في الطريق. تشمل مشاكل اسم النطاق، وشهادات الأمان، وجدار الحماية.
  2. طبقة الطلب: وصل الطلب لكن تفاصيله خاطئة. العنوان، والطريقة، والترويسات، والمحتوى.
  3. طبقة الخادم: وصل الطلب كما يجب لكن الخادم رفضه. منطق خاطئ، أو أذونات، أو قاعدة بيانات.

رموز الحالة التي ستراها كثيراً

الرمزالمعنىالسبب الشائع
400طلب غير صالحمحتوى تالف أو حقل ناقص
401غير مصرّحرمز دخول ناقص أو منتهي
403ممنوعالرمز صالح لكن الصلاحية غير كافية
404غير موجودمسار خاطئ أو طريقة غير مدعومة
405طريقة غير مسموحةاستخدام طلب بدل آخر على المسار نفسه
409تعارضمخالفة قيد التفرّد في قاعدة البيانات
422غير قابل للمعالجةبيانات ناقصة أو خارج النطاق المسموح
429طلبات كثيرةتجاوز حد المعدل المسموح
500خطأ داخلياستثناء لم يُعالَج في الخادم

التمييز الأهم بين 401 و 403. الأول يعني أن الخادم لا يعرف من أنت، والثاني يعني أنه يعرفك لكنك غير مخوّل. هذا يحدد هل المشكلة في رمز الدخول أم في الصلاحيات.

طبقة الشبكة ومشاكل النطاقات

مشكلة النطاق المتقاطع هي أشهر عقبة أمام مطوّري الواجهات. المتصفح يمنع قراءة استجابة من نطاق مختلف إلا إذا سمح الخادم بذلك. الرسالة في الكونسول واضحة:

رسالة نموذجيةtext
Access to fetch at 'https://api.example.com/data'
from origin 'https://myapp.vercel.app' has been blocked by CORS policy:
No 'Access-Control-Allow-Origin' header is present.
  • هذا الخطأ يحدث غالباً بعد وصول الطلب إلى الخادم، أي أن الاستجابة وصلت لكن المتصفح يرفض عرضها.
  • الحل يكون على الخادم بإضافة ترويسة السماح بالنطاق، أو باستخدام وسيط في تطبيقك.
  • عند إرسال بيانات اعتماد، لا يمكنك استخدام علامة النجمة، بل يجب تحديد النطاق بدقة.
  • فشل طلب ما قبل الإرسال غالباً يعني أن الخادم لا يعالج هذا الطلب التمهيدي قبل الطلب الحقيقي.

طبقة الطلب: أخطاء خفيّة

نوع المحتوى لا يطابق المحتوى

إذا أرسلت البيانات بصيغة JSON لكن بدون ترويسة نوع المحتوى، فلن يقرأها الخادم، وستحصل على خطأ 400 أو على محتوى فارغ:

خطأ شائعjavascript
// خطأ: لا توجد ترويسة نوع المحتوى
fetch("/api/items", { method: "POST", body: JSON.stringify(data) });

// صحيح
fetch("/api/items", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify(data)
});

الذاكرة المؤقتة تخفي التغييرات

أحياناً يكون الكود صحيحاً لكن التعديل لا يظهر بسبب ذاكرة المتصفح أو الخدمة. عطّل التخزين المؤقت أثناء التطوير:

تعطيل الذاكرة المؤقتةjavascript
fetch("/api/items", { cache: "no-store" });

// أو أضف معاملاً متغيراً أثناء التطوير
fetch("/api/items?t=" + Date.now());

طبقة الخادم: أين يختبئ خطأ 500

رمز 500 يعني استثناءً لم يُعالَج. ابحث في سجلات الخادم عن أثر الخطأ الكامل بدل التخمين. الأسباب الشائعة:

  • متغير بيئة غير معرّف، فيظهر كقيمة فارغة غير متوقعة في مكان ما.
  • استعلام قاعدة بيانات على عمود غير موجود.
  • محاولة تحليل جسم الطلب وهو فارغ، فتنهار العملية.
  • وعد غير معالَج في دالة لا تدعم الأساليب غير المتزامنة.
  • فشل استدعاء خدمة خارجية، ويُبلّغ كخطأ داخلي رغم أن المشكلة في الشبكة.
معالجة أخطاء متدرجةtypescript
export async function POST(request: Request) {
  try {
    const body = await request.json();

    if (!body?.email) {
      return Response.json({ error: "email_required" }, { status: 422 });
    }

    const result = await save(body);
    return Response.json(result, { status: 201 });
  } catch (err) {
    console.error("POST /api/items failed:", err);
    return Response.json({ error: "internal_error" }, { status: 500 });
  }
}

سجلات تكفي لتشخيص أي خطأ

  1. ولّد معرف طلب فريد في الطبقة الوسيطة لكل طلب.
  2. مرره في ترويسة مخصصة للاستجابة.
  3. سجّله مع كل خطأ في الخادم.
  4. اعرضه في المتصفح عند إرسال بلاغ عن مشكلة.

خطوات التشخيص المتكررة

الخطوةالسؤال الذي تجيب عنه
اقرأ رمز الحالةأي طبقة هي الفاشلة؟
افتح الطلب المرسلما الذي أرسلته فعلاً؟
افتح الاستجابةماذا ردّ الخادم بالضبط؟
راجع الكونسولهل هناك خطأ نطاق أو ترطيب؟
جرّب من الطرفيةهل المشكلة في المتصفح أم في الواجهة؟
اختبار مستقل عن المتصفحbash
curl -i -X POST https://api.example.com/items \
  -H "Content-Type: application/json" \
  -d '{"name":"test"}'