Menu
العربية

Fetch API في JavaScript: طلبات، JSON ومعالجة الأخطاء

تعلّم كيفية استخدام Fetch API في جافا سكريبت لإرسال طلبات GET وPOST، قراءة JSON، التعامل مع الأخطاء بشكل صحيح، وإلغاء الطلبات البطيئة.

Fetch API في جافا سكريبت: عميل HTTP مبني على الـ Promises

الدالة fetch متوفرة بشكل جاهز في المتصفحات وفي الإصدارات الحديثة من Node. تمرّر لها رابط URL، فتُرجع لك Promise يُحَلّ إلى كائن Response. هذه هي فكرة الـ API كلها باختصار:

index.js
Output
Click Run to see the output here.

استدعاءان لـ .then لأنّ العملية تمرّ بخطوتين غير متزامنتين: أولاً تصل ترويسات الاستجابة (وهذا ما يُرجعه الوعد الأول)، ثم تتم قراءة جسم الاستجابة وتحليله (ولاحظ أنّ response.json() هو نفسه وعد). فجسم الاستجابة لا يُحمَّل إلا حين تطلبه صراحةً.

ونفس المنطق مع async/await يبدو وكأنّه كود عادي يُقرأ من الأعلى إلى الأسفل:

index.js
Output
Click Run to see the output here.

مرتان من await، ونقطتا تعليق. نفس الشغل، لكن ترتيب القراءة أوضح.

كائن الاستجابة Response

ما يرجع لك ليس هو محتوى الاستجابة مباشرة، بل كائن Response يحمل بيانات وصفية ومجموعة من الدوال التي تتيح لك قراءة المحتوى بصيغ مختلفة:

index.js
Output
Click Run to see the output here.

يمكنك قراءة محتوى الاستجابة بأحد الأشكال التالية: .json() أو .text() أو .blob() أو .arrayBuffer() أو .formData(). كل واحدة منها تُرجع Promise. ولكن انتبه: يمكنك قراءة الـ body مرة واحدة فقط — إذا استدعيت .json() مرتين على نفس الاستجابة، فإن الاستدعاء الثاني سيرمي خطأً.

المفاجأة الكبرى: أخطاء HTTP لا تُعتبر فشلاً في fetch

هذه النقطة تُربك كل من يبدأ العمل مع fetch. استجابة 404 أو 500 ليست رفضاً (rejection) للـ Promise. الـ Promise يُحَلّ بشكل طبيعي، لكن القيمة response.ok === false. الـ Fetch API يرفض الطلب فقط عندما يفشل الطلب نفسه في الوصول — مثل فشل في DNS، أو انقطاع الشبكة، أو حجب من CORS.

وهذا يعني أن استخدام fetch بشكل ساذج سيمرّر لك صفحة خطأ وكأنها نتيجة ناجحة، ثم ينهار عند استدعاء .json():

index.js
Output
Click Run to see the output here.

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

index.js
Output
Click Run to see the output here.

اعتد على كتابة كتلة if (!response.ok) هذه، فهي يجب أن تكون جزءًا من أي دالة تغليف لـ fetch تكتبها.

إرسال طلب POST باستخدام fetch

الطلب الافتراضي هو GET. أما إذا أردت أي نوع آخر، فمرّر وسيطًا ثانيًا عبارة عن كائن خيارات:

index.js
Output
Click Run to see the output here.

ثلاث نقاط تستحق الانتباه:

  • method قيمته الافتراضية هي "GET". لذا عليك تحديده صراحةً عند استخدام POST أو PUT أو DELETE أو PATCH.
  • body يقبل نصًا (أو FormData أو Blob وغيرها) — دالة fetch لن تحوّل الكائنات إلى JSON نيابةً عنك. استدعاء JSON.stringify(...) مسؤوليتك أنت.
  • ترويسة Content-Type تُخبر الخادم بكيفية قراءة محتوى الطلب. إن أهملتها، ستتعامل أغلب الخوادم مع المحتوى على أنه نص عادي.

الترويسات وسلاسل الاستعلام وبقية الخيارات

الترويسات ما هي إلا كائن عادي (أو نسخة من Headers). أما سلاسل الاستعلام فتبنيها بنفسك، وعادةً عبر URLSearchParams:

index.js
Output
Click Run to see the output here.

URLSearchParams يتكفّل بعملية الترميز نيابةً عنك — المسافات، علامات &، ورموز اليونيكود — فلا ينتهي بك المطاف بروابط مكسورة حين يحتوي المُدخَل على محارف تحتاج إلى هروب.

من الخيارات الأخرى التي ستصادفها في الشيفرات الواقعية: credentials: "include" لإرسال ملفات تعريف الارتباط عبر النطاقات، وcache: "no-store" لتجاوز ذاكرة تخزين HTTP المؤقتة، وmode: "cors" (وهو الوضع الافتراضي عادةً) للتحكّم في سلوك CORS.

إلغاء الطلب باستخدام AbortController

أحيانًا تحتاج إلى التراجع عن الطلب — ربما كتب المستخدم استعلام بحث جديدًا، أو استغرق الطلب وقتًا أطول من اللازم. هنا يأتي دور AbortController:

index.js
Output
Click Run to see the output here.

استدعاء controller.abort() يرفض وعد fetch برمي DOMException يحمل الاسم "AbortError". أما كتلة finally فدورها تنظيف المؤقّت، حتى لا يترك الطلب الناجح مؤقّتًا معلّقًا في الخلفية.

هذا النمط — fetch مع مهلة زمنية مع تنظيف — يستحق أن نغلّفه في دالة مساعدة ونعيد استخدامه في كل مكان.

دالة مساعدة قابلة لإعادة الاستخدام

إذا جمعنا كل ما سبق، نحصل على دالة مساعدة صغيرة تتكفّل بالكود المتكرّر مرّة واحدة وإلى الأبد:

index.js
Output
Click Run to see the output here.

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

الخطوة التالية: معالجة الأخطاء في الكود غير المتزامن

يُعدّ fetch من أكثر الأماكن التي تظهر فيها أخطاء الكود غير المتزامن، وفحص response.ok ما هو إلا قطعة واحدة من الصورة الكاملة. الصفحة التالية تتناول معالجة الأخطاء في الـ Promises وasync/await — أين تذهب الأخطاء، وكيف تلتقطها، وما هي الفخاخ التي تجعلها تمرّ دون أن تلاحظها.

الأسئلة الشائعة

كيف أستخدم fetch في جافا سكريبت؟

تستدعي fetch(url) ومررها الرابط المطلوب، وهي تُرجع Promise يُحَلّ إلى كائن Response. بعدها تستدعي response.json() (وهي أيضًا تُرجع promise) لقراءة محتوى الـ body. مع async/await يصير الكود أبسط: const res = await fetch(url); const data = await res.json();.

كيف أرسل طلب POST باستخدام fetch؟

مرّر وسيطًا ثانيًا يحتوي على method: 'POST'، وكائن headers (عادةً 'Content-Type': 'application/json')، وbody — ولا تنسَ تحويل الكائن إلى نص عبر JSON.stringify(...). لأن fetch لن يقوم بتحويل الـ body نيابةً عنك.

لماذا لا يفشل fetch عند الأخطاء 404 أو 500؟

لأن fetch يرفض الوعد فقط عند أخطاء الشبكة — مثل فشل DNS أو انقطاع الاتصال أو حجب CORS. أما أكواد HTTP الخاطئة فتُعتبر استجابات ناجحة بالنسبة للـ promise. لذا يجب عليك التحقق يدويًا من response.ok (تكون true للأكواد 200–299) أو من response.status، ثم ترمي خطأ بنفسك إذا رجع الخادم باستجابة غير سليمة.

هل يمكنني إلغاء طلب fetch أثناء تنفيذه؟

نعم، عبر AbortController. أنشئ نسخة منه، ومرّر signal الخاصة به إلى fetch ضمن كائن الخيارات، ثم استدعِ controller.abort() وقت ما تريد الإلغاء. عندها سيرفض الـ promise الخاص بـ fetch مع خطأ AbortError يمكنك التقاطه داخل catch.

تعلّم البرمجة مع Coddy

ابدأ الآن