البرمجة مبتدئ 8 دقيقة

كيفية جلب وعرض بيانات API باستخدام JavaScript النقي

Fetch API هي الطريقة الحديثة لإجراء طلبات HTTP في المتصفح. تُعيد Promises، وتتكامل بسلاسة مع async/await، وتأتي مدمجة في كل المتصفحات — لا حاجة لأي مكتبة. لكنها تحمل ثغرة خفية يقع فيها المبتدئون دائماً: fetch لا يُطلق استثناءً عند ردود HTTP الخاطئة. الرد 404 أو 500 هو Promise منجز، لا مرفوض.

يستعرض هذا الدليل النمط الكامل: جلب JSON من API حقيقي، والتحقق من حالة الرد صراحةً، وعرض النتائج في DOM بأمان، ومعالجة حالات التحميل والأخطاء، وإلغاء الطلبات الجارية حتى لا تطغى البيانات القديمة على الحديثة.

تستخدم جميع الأمثلة https://api.github.com/users/octocat — متاح للعموم ولا يتطلب مصادقة.

الخطوات

  1. 1

    أجرِ استدعاء fetch أساسياً

    استدعِ fetch(url) الذي يُعيد Promise ينتهي بكائن Response. استدعِ response.json() لتحليل الجسم — وهو أيضاً Promise. استخدم await للاثنين.

    javascript
    async function getUser() {
      const response = await fetch('https://api.github.com/users/octocat');
      const data = await response.json();
      console.log(data);
    }
    
    getUser();
  2. 2

    تحقق من response.ok — fetch لا يُطلق استثناءً عند الأخطاء

    إذا أعاد السيرفر 404 أو 500، فإن fetch لا يزال ينجز الـ Promise. يُرفض Promise فقط عند أعطال الشبكة (انقطاع الاتصال، أخطاء DNS، حظر CORS). يجب التحقق من response.ok (صحيح للأكواد 200-299) أو من response.status صراحةً، ثم إطلاق خطأ مخصص.

    javascript
    async function getUser(username) {
      const response = await fetch(`https://api.github.com/users/${username}`);
    
      if (!response.ok) {
        throw new Error(`HTTP error: ${response.status} ${response.statusText}`);
      }
    
      return response.json();
    }
  3. 3

    غلّف كل شيء في try/catch

    أخطاء الشبكة، وفشل تحليل JSON، وأي throw صريح من الخطوة السابقة — كلها تظهر كـ Promises مرفوضة. يُعالجها جميعاً كتلة try/catch واحدة في مكان واحد.

    javascript
    async function getUser(username) {
      try {
        const response = await fetch(`https://api.github.com/users/${username}`);
        if (!response.ok) throw new Error(`HTTP ${response.status}`);
        const data = await response.json();
        renderUser(data);
      } catch (err) {
        showError(err.message);
      }
    }
  4. 4

    أظهر حالات التحميل والأخطاء

    اعرض مؤشر التحميل قبل الطلب وأخفِه بعده. عند الخطأ، أظهر رسالة في الواجهة — لا تبتلع الأخطاء بصمت أبداً. ضع عنصر الخطأ في HTML مخفياً بشكل افتراضي.

    javascript
    const output = document.getElementById('output');
    const errorEl = document.getElementById('error');
    const loader = document.getElementById('loader');
    
    async function getUser(username) {
      loader.hidden = false;
      errorEl.hidden = true;
      output.hidden = true;
    
      try {
        const response = await fetch(`https://api.github.com/users/${username}`);
        if (!response.ok) throw new Error(`HTTP ${response.status}`);
        const data = await response.json();
        renderUser(data);
        output.hidden = false;
      } catch (err) {
        errorEl.textContent = `Failed to load: ${err.message}`;
        errorEl.hidden = false;
      } finally {
        loader.hidden = true;
      }
    }
  5. 5

    اعرض البيانات في DOM بأمان

    استخدم textContent لأي نص مصدره المستخدم أو الـ API. يُهرّب HTML تلقائياً — innerHTML لا يفعل ذلك، وقد يُنفّذ سكريبت إذا أعاد الـ API بيانات خبيثة. لا تستخدم innerHTML إلا حين تتحكم 100% في المحتوى.

    javascript
    function renderUser(data) {
      const card = document.getElementById('user-card');
    
      // Safe: textContent escapes HTML entities
      card.querySelector('.name').textContent = data.name ?? data.login;
      card.querySelector('.bio').textContent = data.bio ?? 'No bio.';
      card.querySelector('.repos').textContent = `${data.public_repos} repositories`;
    
      // Safe for URLs you trust (keep validation tight in real apps)
      const avatar = card.querySelector('.avatar');
      avatar.src = data.avatar_url;
      avatar.alt = `Avatar of ${data.login}`;
    }
  6. 6

    ألغِ الطلبات القديمة بـ AbortController

    حين يكتب المستخدم استعلاماً جديداً قبل اكتمال السابق، يجب ألا يحل الرد القديم محل الجديد. يتيح لك AbortController إلغاء الطلبات الجارية. أنشئ controller جديداً لكل استدعاء وألغِ السابق.

    javascript
    let currentController = null;
    
    async function searchUsers(query) {
      // Abort any in-flight request
      if (currentController) currentController.abort();
      currentController = new AbortController();
    
      try {
        const response = await fetch(
          `https://api.github.com/search/users?q=${encodeURIComponent(query)}`,
          { signal: currentController.signal }
        );
        if (!response.ok) throw new Error(`HTTP ${response.status}`);
        const data = await response.json();
        renderResults(data.items);
      } catch (err) {
        if (err.name === 'AbortError') return; // Expected — ignore
        showError(err.message);
      }
    }
  7. 7

    أرسل طلبات POST بجسم JSON

    لطلبات POST/PUT/PATCH، مرر كائن خيارات ثانياً لـfetch. حوّل الجسم إلى نص بـ stringify، واضبط Content-Type: application/json حتى يُحللها السيرفر بشكل صحيح.

    javascript
    async function createRepo(name, isPrivate = false) {
      const response = await fetch('https://api.github.com/user/repos', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'Authorization': `Bearer ${token}`,
        },
        body: JSON.stringify({ name, private: isPrivate }),
      });
    
      if (!response.ok) {
        const err = await response.json();
        throw new Error(err.message ?? `HTTP ${response.status}`);
      }
    
      return response.json();
    }

نصائح ومحاذير

  • تحقق دائماً من <code>response.ok</code> قبل استدعاء <code>response.json()</code> — قد لا يكون جسم الرد 4xx/5xx بتنسيق JSON صالح.
  • استخدم <code>encodeURIComponent()</code> على أي معامل URL مصدره المستخدم لمنع حقن URL.
  • يخزّن المتصفح طلبات GET بقوة. أضف معامل لكسر التخزين المؤقت أو استخدم <code>cache: 'no-store'</code> في خيارات fetch حين تحتاج دائماً إلى بيانات حديثة.
  • للطلبات المتزامنة المتعددة، <code>Promise.all([fetch(a), fetch(b)])</code> يُشغّلها بالتوازي وينتظر الجميع.
  • تجنب إعادة جلب بيانات لم تتغير. Map بسيطة في الذاكرة مفهرسة بالـ URL تُشكّل تخزيناً مؤقتاً خفيفاً على جانب العميل.

خاتمة

تُغطي fetch API كل ما تحتاجه لطلبات HTTP في المتصفح، لكن عليك استخدامها بشكل صحيح. العادات الأساسية: تحقق من response.ok، غلّف بـtry/catch، اعرض بـtextContent، وألغِ بـAbortController. أتقن هذه الأربع ولن تحتاج إلى Axios في أغلب مشاريعك.

#JavaScript #API #Fetch
العودة إلى جميع الأدلة

هل تحتاج مساعدة في مشروعك؟

احجز استشارة مجانية لمدة 30 دقيقة لمناقشة تحدياتك التقنية واستكشاف الحلول معًا.