دليل شامل لـ Fetch API في JavaScript: طلبات POST والرؤوس
عند تطوير تطبيقات الويب الحديثة، يكاد يكون من المؤكد أنك ستحتاج إلى التفاعل مع البيانات الخارجية. قد تكون هذه البيانات قادمة من قاعدة بياناتك الخاصة، أو من واجهات برمجة تطبيقات (APIs) تابعة لجهات خارجية، أو غيرها من المصادر. لقد أحدث ظهور تقنية AJAX (Asynchronous JavaScript and XML) في عام 1999 ثورة في طريقة بناء تطبيقات الويب، حيث قدمت منهجية أفضل بكثير.
قبل AJAX، كان يتعين إعادة تحميل الصفحة بأكملها حتى لإجراء تحديثات بسيطة، مما كان يؤثر سلبًا على تجربة المستخدم. لكن AJAX مكنتنا من جلب المحتوى من الخادم الخلفي (backend) وتحديث عناصر محددة في واجهة المستخدم (UI) دون الحاجة لإعادة تحميل الصفحة بالكامل. هذا التطور ساعد المطورين على تحسين تجربة المستخدم وبناء منصات ويب أكبر وأكثر تعقيدًا.
مقدمة سريعة إلى واجهات برمجة التطبيقات (REST APIs)
نحن نعيش الآن في عصر واجهات برمجة التطبيقات المعمارية (RESTful APIs). ببساطة، تسمح لك واجهة REST API بإرسال البيانات واستقبالها من مخزن بيانات (datastore)، سواء كانت قاعدة بياناتك الخاصة أو خادمًا تابعًا لجهة خارجية مثل واجهة Twitter API. توجد عدة أنواع من طلبات REST API، دعنا نستعرض الأكثر شيوعًا منها والتي ستستخدمها في معظم الحالات:
GET: لجلب البيانات من واجهة برمجة التطبيقات. على سبيل المثال، الحصول على معلومات مستخدمTwitterبناءً على اسم المستخدم الخاص به.POST: لإرسال البيانات إلى واجهة برمجة التطبيقات. على سبيل المثال، إنشاء سجل مستخدم جديد يتضمن الاسم والعمر وعنوان البريد الإلكتروني.PUT: لتحديث سجل موجود ببيانات جديدة. على سبيل المثال، تحديث عنوان البريد الإلكتروني لمستخدم.DELETE: لحذف سجل. على سبيل المثال، حذف مستخدم من قاعدة البيانات.

العناصر الأساسية في كل REST API
تتكون كل واجهة REST API من ثلاثة عناصر رئيسية: الطلب (Request)، والاستجابة (Response)، والرؤوس (Headers).
Request(الطلب): هي البيانات التي ترسلها إلى واجهة برمجة التطبيقات، مثل رقم تعريف طلب (order id) لجلب تفاصيل هذا الطلب.

Response(الاستجابة): هي أي بيانات تتلقاها من الخادم بعد طلب ناجح أو فاشل.

Headers(الرؤوس): بيانات وصفية إضافية تُمرر إلى واجهة برمجة التطبيقات لمساعدة الخادم على فهم نوع الطلب الذي يتعامل معه، مثل “content-type” الذي يحدد نوع المحتوى.

الميزة الحقيقية لاستخدام واجهة REST API هي إمكانية بناء طبقة API واحدة لتتعامل معها تطبيقات متعددة. فإذا كانت لديك قاعدة بيانات ترغب في إدارتها باستخدام تطبيق ويب، وتطبيق جوال، وتطبيق سطح مكتب، فكل ما تحتاجه هو طبقة REST API واحدة. الآن بعد أن فهمت كيفية عمل واجهات REST API، دعنا نرى كيف يمكننا استهلاكها.
التعامل مع البيانات عبر XMLHttpRequest
قبل أن يسيطر تنسيق JSON على عالم تبادل البيانات، كان التنسيق الأساسي هو XML. دالة XMLHttpRequest() في JavaScript هي التي أتاحت جلب البيانات من واجهات برمجة التطبيقات التي كانت تُرجع بيانات XML. لقد مكنتنا XMLHttpRequest من جلب بيانات XML من الخادم الخلفي دون الحاجة لإعادة تحميل الصفحة بأكملها. تطورت هذه الدالة منذ أيامها الأولى التي كانت مقتصرة على XML فقط، وأصبحت الآن تدعم تنسيقات بيانات أخرى مثل JSON والنصوص العادية (plaintext). دعنا نكتب استدعاءً بسيطًا لـ XMLHttpRequest إلى واجهة GitHub API لجلب معلومات ملفي الشخصي:
// function to handle success
function success ( ) {
var data = JSON.parse(this.responseText); //parse the string to JSON
console.log(data);
}
// function to handle error
function error ( err ) {
console.log('Request Failed', err); //error details will be in the "err" object
}
var xhr = new XMLHttpRequest(); //invoke a new instance of the XMLHttpRequest
xhr.onload = success; // call success function if request is successful
xhr.onerror = error; // call error function if request failed
xhr.open('GET', 'https://api.github.com/users/manishmshiva'); // open a GET request
xhr.send(); // send the request to the server.
سيقوم الكود أعلاه بإرسال طلب GET إلى الرابط https://api.github.com/users/manishmshiva لجلب معلومات حسابي على GitHub بتنسيق JSON. إذا كانت الاستجابة ناجحة، فستُطبع بيانات JSON التالية في وحدة التحكم (console):

وإذا فشل الطلب، فستُطبع رسالة الخطأ هذه في وحدة التحكم:

Fetch API: البديل الحديث والأكثر فعالية
تُعد Fetch API إصدارًا أبسط وأسهل في الاستخدام من XMLHttpRequest لاستهلاك الموارد بشكل غير متزامن (asynchronously). تتيح لك Fetch العمل مع واجهات REST API بخيارات إضافية مثل تخزين البيانات مؤقتًا (caching)، وقراءة الاستجابات المتدفقة (streaming responses)، والمزيد. الفرق الرئيسي والجوهري هو أن Fetch تعمل بالوعود (promises) وليس دوال الاستدعاء (callbacks). لقد اتجه مطورو JavaScript بعيدًا عن استخدام دوال الاستدعاء بعد تقديم الوعود، ففي التطبيقات المعقدة، قد ينتهي بك الأمر بسهولة إلى ما يُعرف بـ “جحيم الاستدعاءات” (callback hell). باستخدام الوعود، يصبح من السهل كتابة الطلبات غير المتزامنة والتعامل معها بفعالية.
إليك كيف ستبدو الدالة التي كتبناها سابقًا إذا استخدمت fetch() بدلاً من XMLHttpRequest:
// GET Request.
fetch('https://api.github.com/users/manishmshiva')
// Handle success
.then(response => response.json()) // convert to json
.then(json => console.log(json)) //print data to console
.catch(err => console.log('Request Failed', err)); // Catch errors
يجب أن تكون المعلمة الأولى لدالة Fetch دائمًا هي عنوان URL. بعد ذلك، تأخذ Fetch كائن JSON ثانيًا يتضمن خيارات مثل نوع الطلب (method)، والرؤوس (headers)، وجسم الطلب (request body)، وما إلى ذلك.
هناك فرق مهم بين كائن الاستجابة (response object) في XMLHttpRequest و Fetch. فبينما تُرجع XMLHttpRequest البيانات مباشرة كاستجابة، يحتوي كائن الاستجابة من Fetch على معلومات حول كائن الاستجابة نفسه، بما في ذلك الرؤوس ورمز الحالة (status code) وغيرها. نحن نستدعي دالة res.json() للحصول على البيانات التي نحتاجها من كائن الاستجابة.
فرق آخر مهم هو أن Fetch API لن تُلقي خطأ (throw an error) إذا أعاد الطلب رمز حالة 400 أو 500. بل ستظل تُصنف كاستجابة ناجحة وتُمرر إلى دالة then(). تُلقي Fetch خطأ فقط إذا توقف الطلب نفسه. للتعامل مع استجابات 400 و 500، يمكنك كتابة منطق مخصص باستخدام خاصية response.status، والتي ستمنحك رمز حالة الاستجابة المُرجعة.
ممتاز. الآن بعد أن فهمت كيفية عمل Fetch API، دعنا نلقي نظرة على المزيد من الأمثلة مثل تمرير البيانات والعمل مع الرؤوس.
التعامل مع الرؤوس (Headers)
يمكنك تمرير الرؤوس باستخدام خاصية "headers". يمكنك أيضًا استخدام مُنشئ الرؤوس (headers constructor) لهيكلة الكود بشكل أفضل، ولكن تمرير كائن JSON إلى خاصية "headers" يجب أن يكون كافيًا لمعظم الحالات.
fetch('https://api.github.com/users/manishmshiva', {
method: "GET",
headers: {
"Content-type": "application/json;charset=UTF-8"
}
})
.then(response => response.json())
.then(json => console.log(json))
.catch(err => console.log(err));
تمرير البيانات لطلب POST
لإرسال طلب POST، يمكنك استخدام خاصية "body" لتمرير سلسلة نصية بتنسيق JSON كمدخلات. لاحظ أن جسم الطلب (request body) يجب أن يكون سلسلة JSON نصية، بينما يجب أن تكون الرؤوس (headers) كائن JSON.
// data to be sent to the POST request
let _data = {
title: "foo",
body: "bar",
userId: 1
}
fetch('https://jsonplaceholder.typicode.com/posts', {
method: "POST",
body: JSON.stringify(_data),
headers: {
"Content-type": "application/json; charset=UTF-8"
}
})
.then(response => response.json())
.then(json => console.log(json))
.catch(err => console.log(err));
دعم المتصفحات لـ Fetch API
لا تزال Fetch API قيد التطوير النشط، ونتوقع ظهور ميزات أفضل في المستقبل القريب. ومع ذلك، تدعم معظم المتصفحات استخدام Fetch في تطبيقاتك. يوضح الرسم البياني أدناه المتصفحات التي تدعمها على الويب وتطبيقات الجوال:

الخلاصة
نأمل أن يكون هذا المقال قد ساعدك على فهم كيفية العمل مع Fetch API. تأكد من تجربة Fetch في تطبيق الويب القادم الخاص بك للاستفادة من مزاياها.
الخلاصة التقنية
يمثل الانتقال من XMLHttpRequest إلى Fetch API تطورًا طبيعيًا في عالم تطوير الويب، مدفوعًا بالحاجة إلى تبسيط التعامل مع الطلبات غير المتزامنة. بفضل اعتمادها على الوعود (Promises)، توفر Fetch API نموذجًا برمجيًا أكثر وضوحًا وقابلية للصيانة، مما يقلل من تعقيد “جحيم الاستدعاءات” الذي كان شائعًا. كما أن مرونتها في التعامل مع الرؤوس وأجسام الطلبات، بالإضافة إلى دعمها المدمج لميزات حديثة مثل Streams، يجعلها الخيار المفضل للمطورين اليوم. ومع ذلك، من الضروري تذكر أن Fetch لا تُلقي الأخطاء تلقائيًا لرموز حالة HTTP مثل 4xx أو 5xx، مما يتطلب معالجة يدوية إضافية لضمان قوة التطبيق. إن فهم هذه الفروق الدقيقة واستغلال قوة الوعود يضع المطورين على المسار الصحيح لبناء تطبيقات ويب حديثة وفعالة.