محمد قتيبة شيخانيأبني. أستكشف. أشارك

16من 22

تطوير الويب

واجهة برمجة التطبيقات (API): كيف تطلب صفحتك البيانات من خادمٍ آخر

رسمٌ بخطوطٍ داكنة على خلفيّةٍ كريميّة: صندوقٌ في اليمين اسمه «صفحتك» وتحته fetch(url)، وصندوقٌ في اليسار اسمه «خادم الواجهة» وتحته open-meteo.com. بينهما سهمٌ علويّ إلى اليسار عليه «الطلب» وGET /v1/forecast، وسهمٌ سفليّ أحمر إلى اليمين عليه «الردّ» وتحته {"temperature_2m": 26.1}. وتحت الرسم عنوان المقالة «واجهة برمجة التطبيقات» وسطرٌ يقول: كيف تطلب صفحتك البيانات من خادمٍ آخر.

صفحتك منشورةٌ الآن على الويب منذ المقال السابق، لكنّها تعرف ما في ملفّاتها فقط. فيها عنوانٌ وقائمة وثلاث بطاقات، وزرٌّ يبدّل الوضع الداكن، وكلّ ذلك كتبته بيدك. لكن جرّب أن تضيف إليها سطرًا يقول كم درجة الحرارة الآن في دمشق. هذا الرقم لا يمكن أن يُكتب في index.html، لأنّه يتغيّر كلّ ربع ساعة، ولأنّك لا تملك محطّة أرصاد. وظهيرة الأول من تشرين الأوّل 2026 كان 26.1 درجة، ووصل إلى الصفحة من خادمٍ في مكانٍ آخر، في أقلّ من ثانية.

الطريق الذي سلكه ذلك الرقم اسمه واجهة برمجة التطبيقات، أو API. وفي هذا المقال تتعلّمه كما يعمل فعلًا: تطلب البيانات من الطرفية أوّلًا وتقرأ الردّ بعينك، ثمّ تكتب الشيفرة التي تطلبها من صفحتك وتعرضها. وفي الطريق تعرف الأخطاء الثلاثة التي تقع فيها أغلب المحاولات الأولى، وأخطرها مفتاحٌ سرّيّ يُكتب في شيفرةٍ يقرؤها كلّ زائر.

ما هي واجهة برمجة التطبيقات؟

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

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

وقد استعملت واجهاتٍ كثيرة قبل اليوم، وإن لم تسمّها بهذا الاسم. فحين كتبت document.querySelector في مقال JavaScript، أو حفظت اختيار الوضع الداكن في localStorage، كنت تستعمل واجهاتٍ يبنيها المتصفّح نفسه. وتقسم MDN الواجهات في الويب إلى نوعين: واجهات المتصفّح المبنيّة فيه، والواجهات الخارجيّة التي تأتي بياناتها من مكانٍ على الويب.[1] وهذا المقال عن النوع الثاني: خادمٌ بعيد تسأله فيجيب.

طلبٌ وردّ: ما يجري بين صفحتك والخادم

الحوار مع واجهةٍ على الويب هو الحوار نفسه الذي شرحه مقال HTTP: طلبٌ يخرج، وردٌّ يعود، والفرق أنّ الردّ هنا بياناتٌ تقرؤها شيفرتك، لا صفحةٌ يعرضها المتصفّح.

وفي الطلب أربعة أجزاء. الأوّل الطريقة، وأشهرها GET لطلب البيانات وPOST لإرسالها، والثاني العنوان، وفيه مسار المورد ثمّ معاملاتٌ بعد علامة الاستفهام تحدّد ما تريد بالضبط، كما يشرح مقال عنوان URL. والثالث الترويسات، وهي معلوماتٌ إضافيّة مثل نوع البيانات المقبول أو مفتاح الدخول. والرابع الجسم، ويُستعمل حين ترسل بياناتٍ مع POST.

وفي الردّ ثلاثة أجزاء: رمز حالة يقول هل نجح الطلب، وترويسات تصف الردّ، وجسمٌ فيه البيانات نفسها. والخطأ الأشهر عند المبتدئ أن يقرأ الجسم وينسى الرمز، فيعرض للزائر رسالة خطأٍ كأنّها بيانات.

شكل 1 طلبٌ وردّ صندوقان: في اليمين صفحتك وفيها الأمر fetch، وفي اليسار خادم الواجهة. سهمٌ علويّ من الصفحة إلى الخادم هو الطلب، وعليه الطريقة GET والعنوان ‎/v1/forecast. وسهمٌ سفليّ مميّز من الخادم إلى الصفحة هو الردّ، وعليه الرمز 200 والنوع JSON. صفحتك fetch(url) خادم الواجهة api.open-meteo.com الطلب GET /v1/forecast الردّ 200 · JSON طلبٌ وردّ ثلاثة صناديق من الأعلى إلى الأسفل: صفحتك وفيها fetch، ثمّ بسهم الطلب GET ‎/v1/forecast خادم الواجهة، ثمّ بسهم الردّ المميّز 200 وJSON صفحتك وهي تعرض البيانات. صفحتك fetch(url) الطلب GET خادم الواجهة api.open-meteo.com الردّ 200 · JSON صفحتك تعرض البيانات
طلبٌ واحد وردّه: صفحتك تطلب بالطريقة GET العنوان ‎/v1/forecast من خادم الواجهة، والخادم يردّ برمز 200 وجسمٍ بصيغة JSON. والردّ المميّز هو ما يجب أن تفحصه شيفرتك قبل أن تستعمله.

ما هي صيغة JSON؟

أغلب الواجهات الحديثة تردّ بصيغة JSON، وهي صيغة نصٍّ لتبادل البيانات يحدّدها المعيار RFC 8259، الصادر في كانون الأوّل 2017، والمأخوذة من طريقة JavaScript في كتابة الكائنات.[2] فيها ستّة أنواع فقط: نصٌّ بين علامتي تنصيص مزدوجتين، ورقم، وصحيحٌ أو خطأ، وقيمةٌ فارغة، وقائمةٌ بين قوسين مربّعين، وكائنٌ بين قوسين معقوفين فيه أسماءٌ وقيمها. وهذا جزءٌ حقيقيّ من ردٍّ عن طقس دمشق:

{
  "latitude": 33.5,
  "longitude": 36.3125,
  "current": {
    "time": "2026-10-01T10:00",
    "temperature_2m": 26.1,
    "wind_speed_10m": 8.2
  }
}

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

اقرأ التوثيق قبل أن تطلب

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

وكثيرٌ من الواجهات تنشر عقدها أيضًا في ملفٍّ تقرؤه الآلات، بصيغةٍ اسمها OpenAPI. ويعرّفها معيارها بأنّها وصفٌ موحّد لواجهات HTTP لا يرتبط بلغة برمجة، يفهم منه البشر والحواسيب ما تقدّمه الخدمة من غير أن يقرؤوا شيفرتها. وأحدث إصداراته 3.2.1، نُشر في 10 أيلول 2026.[3] ومن هذا الملفّ تولّد أدواتٌ كثيرة صفحات توثيقٍ تجرّب فيها الطلبات من المتصفّح مباشرةً.

جرّب من الطرفية قبل أن تكتب شيفرة

قبل أن تكتب سطر JavaScript واحدًا، اطلب البيانات من الطرفية وانظر إليها بعينك. فإن ظهر خطأ، تعرف أنّه في الطلب لا في شيفرتك. والواجهة التي يستعملها هذا المقال اسمها Open-Meteo، وهي واجهة طقسٍ لا تطلب مفتاحًا ولا تسجيلًا. وهي مجّانيّة للاستعمال غير التجاريّ حتّى 10,000 طلب في اليوم، وبياناتها مرخّصة بـCC BY 4.0، أي أنّ ذكر مصدرها شرطٌ لاستعمالها.[4]

والجلسة التالية فيها طلبان. الأوّل صحيح، بإحداثيّات دمشق. والثاني فيه خطأٌ متعمّد، خطّ عرضٍ مقداره 333، ومعه الخيار -i الذي يعرض سطر الحالة والترويسات قبل الجسم:

طرفيّة داكنة فيها أمران. الأوّل curl -s إلى عنوان api.open-meteo.com/v1/forecast بخطّ عرض دمشق وطولها، وتحته ردٌّ بصيغة JSON في سطرٍ طويل فيه الإحداثيّات والوحدات، وفي آخره current وفيه درجة الحرارة 26.1 وسرعة الرياح 8.2. والثاني curl -i بخطّ عرضٍ خاطئ هو 333، وتحته سطر الحالة HTTP/1.1 400 Bad Request والترويسات، ثمّ جسمٌ بصيغة JSON فيه error: true وسببٌ يقول إنّ خطّ العرض يجب أن يكون بين -90 و90.
طلبان حقيقيّان إلى Open-Meteo: الأوّل يعيد طقس دمشق بصيغة JSON، والثاني بخطّ عرضٍ مستحيل يعيد الرمز 400 وجسمًا يشرح الخطأ بجملةٍ واضحة. لقطة شاشة من جهازي.

انظر إلى الطلب الثاني جيّدًا. فالخادم لم يسكت ولم يعطِ رقمًا خاطئًا: ردّ بالرمز 400، أي أنّ الطلب نفسه فاسد، ثمّ شرح السبب في جسمٍ بصيغة JSON: خطّ العرض يجب أن يكون بين ‎-90 و90. فالواجهة الجيّدة تتكلّم حين تخطئ أيضًا، وشيفرتك يجب أن تسمع.

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

رموز حالة HTTP خمس عائلات، يحدّدها أوّل رقم: من 200 إلى 299 نجاح، ومن 400 إلى 499 خطأٌ من الطالب، ومن 500 إلى 599 خطأٌ في الخادم.[5] وهذه التي ستقابلها أكثر من غيرها مع الواجهات:

  • الرمز 200: نجح الطلب، والبيانات في الجسم.
  • الرمز 400: الطلب نفسه فاسد، كخطّ العرض 333 في اللقطة السابقة.
  • الرمزان 401 و403: الأوّل يعني أنّك لم تثبت هويّتك، كأن تنسى المفتاح، والثاني أنّ هويّتك معروفة لكنّها لا تملك هذا الحقّ.
  • الرمز 404: المورد غير موجود، وغالبًا في العنوان حرفٌ خاطئ.
  • الرمز 429: أرسلت طلباتٍ كثيرة في وقتٍ قصير، وقد يرسل الخادم معه ترويسة Retry-After تقول كم تنتظر قبل المحاولة التالية.[6]
  • الرمز 500: عطلٌ في الخادم، والذنب ليس ذنبك، فانتظر ثمّ أعد المحاولة.

من الطرفية إلى صفحتك: الدالّة fetch

الطلب الذي نجح في الطرفية تكتبه الآن في صفحة المشروع، داخل script.js. والهدف بطاقةٌ صغيرة عنوانها «الطقس الآن» فيها ثلاث مدن: دمشق والرياض وبرلين. وتسمح Open-Meteo بأن تطلب عدّة أماكن في طلبٍ واحد، فتفصل الإحداثيّات بفواصل، فيصير الردّ قائمةً فيها كائنٌ لكلّ مكان بالترتيب نفسه.[7] وأضف أوّلًا إلى index.html قسمًا فيه قائمةٌ فارغة اسمها weather-list، وتحتها سطر المصدر الذي تشترطه الرخصة:

<section class="weather" aria-labelledby="weather-title">
  <h2 id="weather-title">الطقس الآن</h2>
  <ul id="weather-list">
    <li>جارٍ جلب البيانات…</li>
  </ul>
  <p class="credit">بيانات الطقس من <a href="https://open-meteo.com/">Open-Meteo</a>، برخصة <span class="licence">CC BY 4.0</span>.</p>
</section>

ثمّ الشيفرة في آخر script.js. اقرأها ببطء، فكلّ سطرٍ فيها له سبب:

const cities = [
  { name: 'دمشق', lat: 33.51, lon: 36.29 },
  { name: 'الرياض', lat: 24.71, lon: 46.68 },
  { name: 'برلين', lat: 52.52, lon: 13.40 },
];

async function loadWeather() {
  const list = document.querySelector('#weather-list');
  const url = 'https://api.open-meteo.com/v1/forecast'
    + '?latitude=' + cities.map((c) => c.lat).join(',')
    + '&longitude=' + cities.map((c) => c.lon).join(',')
    + '&current=temperature_2m,wind_speed_10m';

  try {
    const response = await fetch(url, { signal: AbortSignal.timeout(8000) });
    if (!response.ok) {
      throw new Error('ردّ الخادم برمز ' + response.status);
    }
    const data = await response.json();
    list.replaceChildren();
    data.forEach((place, i) => {
      const item = document.createElement('li');
      item.textContent = cities[i].name + ': ' + place.current.temperature_2m
        + ' درجة، والرياح ' + place.current.wind_speed_10m + ' كم/سا';
      list.append(item);
    });
  } catch (error) {
    list.replaceChildren();
    const item = document.createElement('li');
    item.textContent = 'تعذّر جلب الطقس الآن. (' + error.message + ')';
    list.append(item);
  }
}

loadWeather();

الكلمة async تجعل الدالّة تنتظر من غير أن تجمّد الصفحة، والكلمة await تعني: انتظر هنا حتّى يصل الردّ، ثمّ تابع. والسطر الذي يفحص response.ok هو أهمّ سطرٍ في الشيفرة كلّها، لأنّ الدالّة fetch لا تفشل حين يردّ الخادم برمز خطأٍ مثل 404، كما تنبّه MDN في توثيقها. هي تفشل في أخطاء الشبكة فقط، وتعيد الردّ في غيرها كأنّه نجح، ولهذا تفحص الرمز بنفسك، فالخاصّيّة ok صحيحةٌ حين يكون الرمز بين 200 و299.[8]

والخيار signal في السطر نفسه حمايةٌ من الانتظار الأبديّ. فعلى اتّصالٍ بطيء أو متقطّع، وهو حال كثيرٍ من البيوت في سوريا وفي أماكن أخرى، قد يبقى الطلب معلّقًا والصفحة تقول «جارٍ جلب البيانات» بلا نهاية. والدالّة AbortSignal.timeout تلغي الطلب تلقائيًّا بعد المدّة التي تحدّدها، هنا ثماني ثوانٍ، فيفشل بخطأٍ اسمه TimeoutError، ويصل إلى catch، فتظهر للزائر جملةٌ واضحة بدل الانتظار.[9]

والتفصيل الثاني أنّ النصّ يُكتب في الصفحة بالخاصّيّة textContent لا بـinnerHTML. فالبيانات تأتي من خادمٍ لا تملكه، ولو احتوت يومًا على وسوم HTML لعرضها textContent نصًّا عاديًّا، أمّا innerHTML فينفّذها في صفحتك. والتفصيل الثالث أنّ سطر المصدر جزءٌ من البطاقة، لا ملاحظةٌ في مكانٍ آخر، لأنّ الرخصة تشترطه. وهكذا ظهرت البطاقة على الهاتف:

بطاقةٌ بإطارٍ أحمر رفيع على خلفيّةٍ كريميّة، عنوانها «الطقس الآن»، وتحته ثلاثة أسطر: دمشق 26.1 درجة والرياح 8.2 كم/سا، والرياض 39.7 درجة والرياح 8.5 كم/سا، وبرلين 20.8 درجة والرياح 12.3 كم/سا. وتحتها سطرٌ صغير يقول إنّ بيانات الطقس من Open-Meteo برخصة CC BY 4.0.
بطاقة «الطقس الآن» في صفحة المشروع على الهاتف، ببياناتٍ حيّة لدمشق والرياض وبرلين من طلبٍ واحد، وتحتها سطر المصدر والرخصة. لقطة من المتصفّح.

حين ترسل بدل أن تطلب: الطريقة POST

كلّ ما سبق قراءة. لكنّ نموذج تواصلٍ أو تعليقًا أو تسجيل دخول يرسل بياناتٍ إلى الخادم، وهنا تأتي الطريقة POST. فتعطي fetch معاملًا ثانيًا فيه الطريقة، وترويسة Content-Type تقول للخادم إنّ الجسم بصيغة JSON، والجسم نفسه نصًّا تصنعه الدالّة JSON.stringify من كائن JavaScript.[8] والعنوان هنا مثالٌ توضيحيّ لا واجهة حقيقيّة:

const response = await fetch('https://example.org/api/messages', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ name: 'سلمى', message: 'مرحبًا' }),
});

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

لا تسأل الخادم في كلّ زيارة

الشيفرة السابقة تطلب الطقس كلّما فتح أحدٌ الصفحة. وهذا يكفي لصفحة تدريب، لكنّه يصطدم بالحدود سريعًا. فإن زار صفحتك 10,000 شخص في يوم، أرسلت 10,000 طلب، وهذا كلّ ما تسمح به Open-Meteo مجّانًا. والمفارقة أنّ أغلب تلك الطلبات بلا فائدة، فالردّ نفسه يقول في الحقل interval إنّ القيم الحاليّة تُحدَّث كلّ 900 ثانية، أي كلّ ربع ساعة.

والحلّ اسمه التخزين المؤقّت: تحفظ آخر ردٍّ ووقته، في localStorage مثلًا، ولا تطلب من جديد إلّا إن مرّت ربع ساعة. فيقرأ الزائر الثاني والعاشر ما حفظه متصفّحه، ويصل إلى الخادم طلبٌ واحد كلّ ربع ساعة من كلّ زائر على الأكثر. وكلّ واجهةٍ لها حدودها في توثيقها، وتجاوزها ينتهي غالبًا بالرمز 429 الذي رأيته في قائمة الرموز.

لماذا ينجح الطلب في الطرفية ويفشل في الصفحة؟

ستمرّ بهذا الموقف حتمًا: الطلب نفسه، بالعنوان نفسه، ينجح في الطرفية ويفشل في صفحتك. ولمعرفة السبب جرّبت صفحةُ تجربةٍ على هذا الجهاز أن تطلب بـfetch الصفحة الرئيسة لموقع example.com، فكتب Chrome في الكونسول هذه الرسالة بنصّها:

Access to fetch at 'https://example.com/' from origin 'http://127.0.0.1:8765'
has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header
is present on the requested resource.

وفي الصفحة نفسها لم تصل إلى الشيفرة إلّا جملةٌ مقتضبة: TypeError: Failed to fetch، أمّا curl فجلب العنوان نفسه في اللحظة نفسها برمز 200 بلا أيّ اعتراض. والسبب سياسة أمانٍ في المتصفّح وحده: تقيّد المتصفّحات الطلبات التي ترسلها السكربتات إلى أصلٍ غير أصل الصفحة، والأصل هو البروتوكول والنطاق والمنفذ معًا. ولا تسلّم الردّ للشيفرة إلّا إن قال الخادم صراحةً إنّه يسمح بذلك، بترويسةٍ اسمها Access-Control-Allow-Origin. وهذه الآليّة اسمها CORS.[10] وبعض الطلبات، كالتي ترسل JSON بالطريقة POST، يسبقها المتصفّح بطلبٍ تمهيديّ من نوع OPTIONS يسأل فيه الخادم: هل تقبل هذا؟ فإن لم يأتِ الجواب الصحيح لم يُرسل الطلب الحقيقيّ أصلًا.

شكل 2 الطلب نفسه مرّتين صفّان. في الصفّ العلويّ تطلب الطرفية بالأمر curl من خادم الواجهة، فيصل الردّ كاملًا. وفي الصفّ السفليّ تطلب صفحةٌ في المتصفّح بالأمر fetch من الخادم نفسه، فيصل ردٌّ بلا ترويسة السماح، فيحجبه المتصفّح عن شيفرة الصفحة، وهذا الصندوق المميّز. الطرفية curl خادم الواجهة يصل الردّ كاملًا صفحةٌ في المتصفّح fetch خادم الواجهة يحجبه المتصفّح بلا ترويسة السماح الطلب نفسه مرّتين صفّان. في الصفّ العلويّ تطلب الطرفية بالأمر curl من خادم الواجهة، فيصل الردّ كاملًا. وفي الصفّ السفليّ تطلب صفحةٌ في المتصفّح بالأمر fetch من الخادم نفسه، فيصل ردٌّ بلا ترويسة السماح، فيحجبه المتصفّح عن شيفرة الصفحة، وهذا الصندوق المميّز. الطرفية curl خادم الواجهة يصل الردّ كاملًا صفحةٌ في المتصفّح fetch خادم الواجهة يحجبه المتصفّح الردّ السفليّ بلا ترويسة السماح
الطلب نفسه مرّتين: من الطرفية يصل الردّ كاملًا، ومن صفحةٍ في المتصفّح يصل ردٌّ بلا ترويسة السماح، فيحجبه المتصفّح عن شيفرة الصفحة.

وOpen-Meteo ترسل هذه الترويسة بقيمة النجمة، أي أنّها تسمح لكلّ الأصول، ولهذا عملت بطاقة الطقس. والدرس العمليّ: إن فشل طلبك بهذه الرسالة فالحلّ ليس في صفحتك: فإمّا أنّ الواجهة مصمَّمة لتُطلب من الخوادم لا من المتصفّحات، وإمّا أنّ صاحبها لم يسمح لأصلك. ولا تنقل طلباتك عبر وسيطٍ مجهول وجدته في منتدى لتتجاوز الرسالة، لأنّك تمرّر بياناتك ومفاتيحك عبر خادم شخصٍ لا تعرفه.

المفتاح السرّيّ لا يُكتب في الصفحة

وواجهة Open-Meteo لا تطلب مفتاحًا، لكنّ أغلب الواجهات تطلبه: سلسلةٌ طويلة تعرّف الخادم بك، فيحسب عليك الطلبات ويحاسبك عليها. وهنا يقع الخطأ الأخطر في البدايات، وتراه في شروحٍ كثيرة: مفتاحٌ سرّيّ مكتوبٌ في ملف JavaScript الذي يُرسل إلى المتصفّح. وكلّ ما يصل إلى المتصفّح يقرؤه أيّ زائر، يكفيه أن يفتح مصدر الصفحة أو تبويب الشبكة في أدوات المطوّر.

ووثائق Stripe، شركة المدفوعات، تشرح الفرق بأوضح طريقة، فعندها نوعان من المفاتيح: مفتاحٌ منشور يبدأ بـpk_، يُوضع في شيفرة الواجهة الأماميّة، ولا يستطيع إلّا أعمالًا محدودة. ومفتاحٌ سرّيّ يبدأ بـsk_ وله صلاحيّاتٌ كاملة. وتقول بنصّها إنّ المفاتيح المنشورة وحدها آمنةٌ خارج الخادم الخلفيّ لتطبيقك، وتطلب ألّا تُكتب المفاتيح في شيفرةٍ أو ملفّات إعدادٍ تُحفظ في نظام إدارة الإصدارات.[11]

فالمفتاح السرّيّ مكانه خادمٌ تملكه، أو دالّةٌ صغيرة تعمل على خادم: صفحتك تطلب منها، وهي تضيف المفتاح وتطلب من الواجهة، ثمّ تعيد إلى الصفحة البيانات وحدها. ولا يُرفع المفتاح إلى GitHub أبدًا، ولو كان المستودع خاصًّا، فمكانه ملفٌّ تضعه في ‎.gitignore كما شرح مقال Git. وإن تسرّب مفتاحٌ يومًا، فاحذفه من لوحة الخدمة وأنشئ غيره فورًا، لأنّ حذفه من الشيفرة لا يمحوه من تاريخ المستودع.

واجهة هذا الموقع نفسه

الواجهات ليست حكرًا على شركاتٍ كبيرة. فهذا الموقع مبنيٌّ على ووردبريس، وفي كلّ موقع ووردبريس واجهةٌ تتبادل البيانات بصيغة JSON، وعليها يقوم محرّر المقالات نفسه، كما يقول توثيقها.[12] وعنوانها يبدأ بـ‎/wp-json/، وتستطيع أن تجرّبها الآن على هذا الموقع، فتطلب آخر مقالتين وحقولًا محدّدة منهما فقط:

طرفيّة داكنة فيها الأمر curl -s إلى kutaibashikhani.com/wp-json/wp/v2/posts مع per_page=2 والحقول id وdate وlink، وتحته مصفوفة JSON فيها مقالتان برقميهما 7120 و7073 وتاريخيهما ورابطيهما. ثمّ الأمر curl -sI على العنوان نفسه مع grep، وتحته سطران: x-wp-total: 145 وx-wp-totalpages: 15.
واجهة ووردبريس على هذا الموقع: طلبٌ يعيد آخر مقالتين بأرقامهما وتواريخهما وروابطهما، ثمّ ترويسة x-wp-total تقول إنّ في الموقع 145 مقالة منشورة يوم 1 تشرين الأوّل 2026. لقطة شاشة من جهازي.

والمعامل _fields يطلب الحقول التي تريدها وحدها، فيصغر الردّ كثيرًا، والترويسة x-wp-total تحمل العدد الكلّيّ من غير أن تطلب المقالات كلّها، وكان 145 يوم التقاط الصورة. وهذه القراءة متاحةٌ لأيّ أحد لأنّ المقالات منشورةٌ أصلًا، أمّا الكتابة والتعديل فتحتاج إلى إثبات هويّة. وإن كنت ستبني يومًا موقعًا على ووردبريس، فهذه الواجهة طريقك إلى عرض مقالاته في تطبيقٍ أو صفحةٍ أخرى.

ما هو REST؟

لاحظ شكل العنوان في واجهة ووردبريس: ‎/wp/v2/posts لكلّ المقالات، وتضيف الرقم في آخره، مثل ‎/wp/v2/posts/7120، فتحصل على مقالةٍ واحدة. وهذا الشكل له اسم، REST، وصفه روي فيلدينغ في الفصل الخامس من أطروحة الدكتوراه التي قدّمها سنة 2000.[13]

والفكرة في REST أنّ كلّ شيءٍ موردٌ له عنوان، وأنّ الطرق القليلة نفسها تعمل على كلّ الموارد: فـGET يقرأ، وPOST ينشئ، وPUT أو PATCH يعدّل، وDELETE يحذف. ويضيف فيلدينغ قيدًا مهمًّا: كلّ طلبٍ يجب أن يحمل كلّ المعلومات اللازمة لفهمه، ولا يعتمد على شيءٍ حفظه الخادم من طلبٍ سابق. وهذا هو المقصود بالواجهة التي لا تتذكّرك، والسبب الذي يجعلها سهلة التوسيع على آلاف الخوادم.

وREST ليس الأسلوب الوحيد. فهناك GraphQL مثلًا، تطلب فيه الحقول التي تريدها في طلبٍ واحد بلغة استعلامٍ خاصّة. لكنّ أغلب ما ستقابله في سنتك الأولى واجهاتٌ على طريقة REST، ترسل إليها طلبات HTTP عاديّة وتعيد JSON، كالتي جرّبتها في هذا المقال.

خلاصة عمليّة

اقرأ توثيق الواجهة قبل كلّ شيء، واطلب من الطرفية بـcurl قبل أن تكتب شيفرة، ثمّ افحص response.ok في كلّ طلب، لأنّ fetch لا يفشل مع 404 ولا مع 500. اكتب البيانات في الصفحة بـtextContent، واذكر المصدر حين تشترطه الرخصة. وإن رأيت رسالة CORS، فالحلّ عند صاحب الواجهة أو في خادمك، لا في صفحتك. والمفتاح السرّيّ لا يدخل ملفًّا يصل إلى المتصفّح، ولا مستودعًا على GitHub.

وصفحتك الآن تسأل خادمًا آخر وتعرض جوابه، وتلك أوّل مرّةٍ تخرج فيها بياناتٌ من صفحتك وتدخل إليها من الخارج. ومع هذا الباب يأتي سؤال الأمان كلّه: ما الذي يستطيع غيرك أن يحقنه في صفحتك، وما الذي يتسرّب منها، وهذا موضوع المقال التالي في السلسلة، عن أمان الواجهة الأماميّة.

المصادر

  1. موسوعة MDN، «Introduction to web APIs»: واجهات المتصفّح مبنيّةٌ فيه، والواجهات الخارجيّة ليست فيه وتُجلب شيفرتها وبياناتها من مكانٍ على الويب.
  2. تيم براي (محرّر)، «RFC 8259: The JavaScript Object Notation (JSON) Data Interchange Format»، IETF، كانون الأوّل 2017: صيغة نصٍّ خفيفة لتبادل البيانات مأخوذة من معيار ECMAScript، وتحلّ محلّ RFC 7159.
  3. مبادرة OpenAPI، «OpenAPI Specification v3.2.1»، 10 أيلول 2026: وصفٌ موحّد لواجهات HTTP لا يرتبط بلغة برمجة، يكتشف منه البشر والحواسيب قدرات الخدمة من غير الوصول إلى شيفرتها.
  4. موقع Open-Meteo و«Terms of Use»، قُرئا في 1 تشرين الأوّل 2026: لا مفتاح ولا تسجيل، والاستعمال غير التجاريّ مجّانيّ حتّى 10,000 طلب في اليوم و5,000 في الساعة و600 في الدقيقة، والبيانات برخصة CC BY 4.0 وذكر المصدر شرط.
  5. موسوعة MDN، «HTTP response status codes»: عائلات الردود الخمس، من الإعلاميّة إلى أخطاء الخادم.
  6. موسوعة MDN، «429 Too Many Requests»: أرسل الطالب طلباتٍ كثيرة في وقتٍ محدّد، وقد تأتي معه ترويسة Retry-After تحدّد مدّة الانتظار.
  7. توثيق Open-Meteo، «Weather Forecast API»: يمكن فصل عدّة إحداثيّات بفواصل، فيصير ردّ JSON قائمةً فيها بنيةٌ لكلّ مكان.
  8. موسوعة MDN، «Using the Fetch API»: الوعد الذي تعيده fetch يُرفض في أخطاءٍ مثل خطأ الشبكة، أمّا إن ردّ الخادم بخطأٍ مثل 404 فيتحقّق بردٍّ عاديّ، فتفحص الحالة بنفسك؛ وresponse.ok صحيحة حين يكون الرمز في نطاق 200.
  9. موسوعة MDN، «AbortSignal: timeout() static method»: إشارةٌ تُلغي الطلب تلقائيًّا بعد مدّةٍ محدّدة، بخطأٍ من نوع TimeoutError.
  10. موسوعة MDN، «Cross-Origin Resource Sharing (CORS)»: لأسباب الأمان تقيّد المتصفّحات طلبات HTTP التي تبدؤها السكربتات إلى أصلٍ آخر، والأصل هو النطاق والبروتوكول والمنفذ، إلّا إن تضمّن ردّ الأصل الآخر ترويسات CORS الصحيحة.
  11. توثيق Stripe، «API keys»: المفتاح المنشور pk_ يُوضع في شيفرة الواجهة الأماميّة ولا يستطيع العمليّات الحسّاسة، والمفتاح السرّيّ sk_ له صلاحيّاتٌ كاملة؛ والمفاتيح المنشورة وحدها آمنةٌ خارج الخادم الخلفيّ، ولا تُكتب المفاتيح في شيفرةٍ أو ملفّات إعداد تُحفظ في إدارة الإصدارات.
  12. توثيق ووردبريس، «REST API Handbook»: واجهةٌ تتبادل بها التطبيقات البيانات مع موقع ووردبريس بصيغة JSON، وهي أساس محرّر المكوّنات.
  13. روي فيلدينغ، «Representational State Transfer (REST)»، الفصل الخامس من أطروحة الدكتوراه، جامعة كاليفورنيا في إرفاين، 2000: الواجهة الموحّدة، وأنّ كلّ طلبٍ يحمل كلّ ما يلزم لفهمه بلا حالةٍ محفوظة على الخادم.

اترك تعليقاً

الحقول المميّزة بـ مطلوبة