كل خطأ في واجهة برمجية يقع في واحدة من ثلاث طبقات: الشبكة، أو الطلب نفسه، أو الخادم. تقسيم المشكلة إلى هذه الطبقات يوفّر عليك ساعات من التخمين. هذا الدليل يتبع هذا التقسيم خطوة بخطوة.
الطبقات الثلاث وأين يقع خطأك
- طبقة الشبكة: لم يصل الطلب أصلاً أو انقطع في الطريق. تشمل مشاكل اسم النطاق، وشهادات الأمان، وجدار الحماية.
- طبقة الطلب: وصل الطلب لكن تفاصيله خاطئة. العنوان، والطريقة، والترويسات، والمحتوى.
- طبقة الخادم: وصل الطلب كما يجب لكن الخادم رفضه. منطق خاطئ، أو أذونات، أو قاعدة بيانات.
رموز الحالة التي ستراها كثيراً
| الرمز | المعنى | السبب الشائع |
|---|---|---|
| 400 | طلب غير صالح | محتوى تالف أو حقل ناقص |
| 401 | غير مصرّح | رمز دخول ناقص أو منتهي |
| 403 | ممنوع | الرمز صالح لكن الصلاحية غير كافية |
| 404 | غير موجود | مسار خاطئ أو طريقة غير مدعومة |
| 405 | طريقة غير مسموحة | استخدام طلب بدل آخر على المسار نفسه |
| 409 | تعارض | مخالفة قيد التفرّد في قاعدة البيانات |
| 422 | غير قابل للمعالجة | بيانات ناقصة أو خارج النطاق المسموح |
| 429 | طلبات كثيرة | تجاوز حد المعدل المسموح |
| 500 | خطأ داخلي | استثناء لم يُعالَج في الخادم |
التمييز الأهم بين 401 و 403. الأول يعني أن الخادم لا يعرف من أنت، والثاني يعني أنه يعرفك لكنك غير مخوّل. هذا يحدد هل المشكلة في رمز الدخول أم في الصلاحيات.
طبقة الشبكة ومشاكل النطاقات
مشكلة النطاق المتقاطع هي أشهر عقبة أمام مطوّري الواجهات. المتصفح يمنع قراءة استجابة من نطاق مختلف إلا إذا سمح الخادم بذلك. الرسالة في الكونسول واضحة:
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 أو على محتوى فارغ:
// خطأ: لا توجد ترويسة نوع المحتوى
fetch("/api/items", { method: "POST", body: JSON.stringify(data) });
// صحيح
fetch("/api/items", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(data)
});الذاكرة المؤقتة تخفي التغييرات
أحياناً يكون الكود صحيحاً لكن التعديل لا يظهر بسبب ذاكرة المتصفح أو الخدمة. عطّل التخزين المؤقت أثناء التطوير:
fetch("/api/items", { cache: "no-store" });
// أو أضف معاملاً متغيراً أثناء التطوير
fetch("/api/items?t=" + Date.now());طبقة الخادم: أين يختبئ خطأ 500
رمز 500 يعني استثناءً لم يُعالَج. ابحث في سجلات الخادم عن أثر الخطأ الكامل بدل التخمين. الأسباب الشائعة:
- متغير بيئة غير معرّف، فيظهر كقيمة فارغة غير متوقعة في مكان ما.
- استعلام قاعدة بيانات على عمود غير موجود.
- محاولة تحليل جسم الطلب وهو فارغ، فتنهار العملية.
- وعد غير معالَج في دالة لا تدعم الأساليب غير المتزامنة.
- فشل استدعاء خدمة خارجية، ويُبلّغ كخطأ داخلي رغم أن المشكلة في الشبكة.
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 });
}
}سجلات تكفي لتشخيص أي خطأ
- ولّد معرف طلب فريد في الطبقة الوسيطة لكل طلب.
- مرره في ترويسة مخصصة للاستجابة.
- سجّله مع كل خطأ في الخادم.
- اعرضه في المتصفح عند إرسال بلاغ عن مشكلة.
خطوات التشخيص المتكررة
| الخطوة | السؤال الذي تجيب عنه |
|---|---|
| اقرأ رمز الحالة | أي طبقة هي الفاشلة؟ |
| افتح الطلب المرسل | ما الذي أرسلته فعلاً؟ |
| افتح الاستجابة | ماذا ردّ الخادم بالضبط؟ |
| راجع الكونسول | هل هناك خطأ نطاق أو ترطيب؟ |
| جرّب من الطرفية | هل المشكلة في المتصفح أم في الواجهة؟ |
curl -i -X POST https://api.example.com/items \
-H "Content-Type: application/json" \
-d '{"name":"test"}'