Nikode
جاوا اسکریپت

آموزش Fetch API و دیباگ شبکه

یادگیری کامل Fetch API، درک درخواست‌های شبکه و دیباگ با Chrome DevTools. نحوه ارتباط فرانت‌اند با API های بک‌اند

🌐 مقدمه‌ای بر ارتباط شبکه

وقتی به یک وب‌سایت سر می‌زنید، مرورگر شما تعداد زیادی درخواست شبکه ارسال می‌کند تا داده‌هایی مانند تصاویر، استایل‌ها، اسکریپت‌ها و اطلاعات API را دریافت کند. درک نحوه کارکرد این درخواست‌ها برای توسعه وب مدرن ضروری است.

آنچه یاد خواهید گرفت

  • مبانی HTTP: نحوه کارکرد درخواست‌ها و پاسخ‌ها
  • Fetch API: روش مدرن ارسال درخواست‌های شبکه از JavaScript
  • Chrome DevTools: دیباگ درخواست‌های شبکه مثل حرفه‌ای‌ها
  • ادغام API: اتصال فرانت‌اند به سرویس‌های بک‌اند
  • مدیریت خطا: برخورد مناسب با مشکلات شبکه

چرا این موضوع مهم است؟

هر اپلیکیشن وب مدرن با سرورها ارتباط برقرار می‌کند تا:

  • اطلاعات کاربر را بارگذاری کند
  • فرم‌ها را ذخیره کند
  • به‌روزرسانی‌های لحظه‌ای دریافت کند
  • فایل‌ها را آپلود کند
  • کاربران را احراز هویت کند

📡 درک درخواست‌های HTTP

HTTP چیست؟

HTTP (پروتکل انتقال ابرمتن) روشی است که مرورگرهای وب و سرورها از طریق آن ارتباط برقرار می‌کنند.

ساختار پایه درخواست HTTP

GET /api/users HTTP/1.1
Host: api.example.com
User-Agent: Mozilla/5.0...
Accept: application/json

متدهای HTTP

// GET - دریافت داده
GET / api / users; // دریافت همه کاربران
GET / api / users / 123; // دریافت کاربر خاص

// POST - ایجاد داده جدید
POST / api / users; // ایجاد کاربر جدید

// PUT - به‌روزرسانی کامل منبع
PUT / api / users / 123; // به‌روزرسانی کامل کاربر

// PATCH - به‌روزرسانی جزئی داده
PATCH / api / users / 123; // به‌روزرسانی جزئی کاربر

// DELETE - حذف داده
DELETE / api / users / 123; // حذف کاربر

کدهای وضعیت HTTP

// موفقیت (2xx)
200 OK                 // درخواست موفق
201 Created           // منبع ایجاد شد
204 No Content       // موفق، بدون داده برگشتی

// خطاهای کلاینت (4xx)
400 Bad Request       // درخواست نامعتبر
401 Unauthorized      // احراز هویت نشده
403 Forbidden         // مجاز نیست
404 Not Found         // منبع پیدا نشد
429 Too Many Requests // محدودیت نرخ

// خطاهای سرور (5xx)
500 Internal Server Error // خطای سرور
502 Bad Gateway          // خطای دروازه
503 Service Unavailable  // سرور بیش از حد بارگذاری شده

🚀 مبانی Fetch API

Fetch API روش مدرن ارسال درخواست‌های HTTP از JavaScript است.

درخواست GET ساده

// Basic fetch request
fetch("https://jsonplaceholder.typicode.com/users/1")
  .then((response) => response.json())
  .then((data) => console.log("User:", data))
  .catch((error) => console.error("Error:", error));

درک زنجیره Promise در Fetch

fetch("https://api.example.com/data")
  .then((response) => {
    // Step 1: Check if request was successful
    if (!response.ok) {
      throw new Error(`HTTP error! Status: ${response.status}`);
    }
    return response.json(); // Parse JSON
  })
  .then((data) => {
    // Step 2: Work with parsed data
    console.log("Data received:", data);
    return data;
  })
  .catch((error) => {
    // Step 3: Handle errors
    console.error("Fetch request failed:", error);
  });

نسخه Async/Await (تمیزتر)

async function fetchUser(userId) {
  try {
    const response = await fetch(
      `https://jsonplaceholder.typicode.com/users/${userId}`
    );

    if (!response.ok) {
      throw new Error(`HTTP ${response.status}: ${response.statusText}`);
    }

    const user = await response.json();
    console.log("User:", user);
    return user;
  } catch (error) {
    console.error("Failed to fetch user:", error);
    throw error;
  }
}

// Usage
fetchUser(1).then((user) => console.log("Success:", user));

📤 ارسال انواع مختلف درخواست‌ها

درخواست GET (دریافت داده)

// Simple GET
const response = await fetch("https://api.example.com/users");

// GET with search parameters
const params = new URLSearchParams({
  page: 1,
  limit: 10,
  search: "john",
});
const response = await fetch(`https://api.example.com/users?${params}`);

درخواست POST (ایجاد داده)

const newUser = {
  name: "John Doe",
  email: "john@example.com",
  age: 30,
};

const response = await fetch("https://api.example.com/users", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    Authorization: "Bearer <token>",
  },
  body: JSON.stringify(newUser),
});

const createdUser = await response.json();
console.log("User created:", createdUser);

درخواست PUT (به‌روزرسانی کامل منبع)

const updatedUser = {
  name: "John Doe",
  email: "john@example.com",
  age: 31,
};

const response = await fetch("https://api.example.com/users/123", {
  method: "PUT",
  headers: {
    "Content-Type": "application/json",
  },
  body: JSON.stringify(updatedUser),
});

درخواست DELETE (حذف داده)

const response = await fetch("https://api.example.com/users/123", {
  method: "DELETE",
  headers: {
    Authorization: "Bearer your-token-here",
  },
});

if (response.ok) {
  console.log("User successfully deleted");
}

آپلود فایل‌ها

const fileInput = document.getElementById("file-input");
const file = fileInput.files[0];

const formData = new FormData();
formData.append("file", file);
formData.append("description", "My uploaded file");

const response = await fetch("https://api.example.com/upload", {
  method: "POST",
  body: formData, // Don't set Content-Type header - browser sets it automatically
});

🔧 ویژگی‌های پیشرفته Fetch

گزینه‌های درخواست

const requestOptions = {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    Authorization: "Bearer your-token",
    Accept: "application/json",
  },
  body: JSON.stringify(data),
  mode: "cors", // 'cors', 'no-cors', 'same-origin'
  credentials: "include", // 'omit', 'same-origin', 'include'
  cache: "no-cache", // 'default', 'no-cache', 'reload', 'force-cache'
  redirect: "follow", // 'follow', 'error', 'manual'
};

const response = await fetch("https://api.example.com/data", requestOptions);

مدیریت انواع مختلف پاسخ

async function handleResponse(url) {
  const response = await fetch(url);

  if (!response.ok) {
    throw new Error(`HTTP ${response.status}: ${response.statusText}`);
  }

  // Check content type
  const contentType = response.headers.get("content-type");

  if (contentType.includes("application/json")) {
    return await response.json();
  } else if (contentType.includes("text/")) {
    return await response.text();
  } else {
    return await response.blob(); // For files
  }
}

پیاده‌سازی تایم‌اوت

function fetchWithTimeout(url, options = {}, timeout = 5000) {
  return Promise.race([
    fetch(url, options),
    new Promise((_, reject) =>
      setTimeout(() => reject(new Error("Request timeout")), timeout)
    ),
  ]);
}

// Usage
try {
  const data = await fetchWithTimeout("https://api.example.com/data", {}, 3000);
  console.log("Data:", data);
} catch (error) {
  console.error("Request failed:", error.message);
}

منطق تلاش مجدد

async function fetchWithRetry(url, options = {}, maxRetries = 3) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      const response = await fetch(url, options);

      if (!response.ok) {
        throw new Error(`HTTP ${response.status}`);
      }

      return await response.json();
    } catch (error) {
      if (attempt === maxRetries) {
        throw error;
      }

      // Wait before retry (exponential backoff)
      await new Promise((resolve) => setTimeout(resolve, 1000 * attempt));
      console.log(`Attempt ${attempt} failed, retrying...`);
    }
  }
}

🛠️ تب Network در Chrome DevTools

تب Network در Chrome DevTools بهترین دوست شما برای دیباگ درخواست‌های شبکه است.

باز کردن تب Network

  1. Chrome DevTools را باز کنید (F12 یا Ctrl+Shift+I)
  2. روی تب Network کلیک کنید
  3. صفحه را رفرش کنید یا عملی را تحریک کنید

درک تب Network

لیست درخواست‌ها

Name                    Method  Status  Type     Size    Time
main.js                 GET     200     JS      15.2KB  45ms
styles.css              GET     200     CSS     8.1KB   32ms
api/users               GET     200     XHR     2.3KB   156ms
profile.jpg             GET     200     Img     45.2KB  89ms

جزئیات درخواست

// Headers tab
Request Headers:
GET /api/users HTTP/1.1
Host: api.example.com
User-Agent: Mozilla/5.0...
Accept: application/json
Authorization: Bearer token123

Response Headers:
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-cache

فیلتر کردن درخواست‌ها

// Filter by request type
- All
- XHR (API requests)
- JS (JavaScript files)
- CSS (Stylesheets)
- Img (Images)
- Media (Audio/Video)
- Font (Web fonts)
- Doc (Documents)
- WS (WebSocket)

ویژگی‌های تب Network

۱. حفظ لاگ

  • درخواست‌ها هنگام ناوبری قابل مشاهده می‌مانند
  • مفید برای دیباگ اپلیکیشن‌های تک صفحه‌ای

۲. غیرفعال کردن کش

  • درخواست‌های تازه را اجباری می‌کند
  • خوب برای تست تغییرات API

۳. محدود کردن سرعت

  • شبیه‌سازی اتصالات کند
  • تست عملکرد اپ در موبایل

۴. مسدود کردن درخواست

  • مسدود کردن درخواست‌های خاص
  • تست مدیریت خطا

🔍 دیباگ درخواست‌های شبکه

مشکلات رایج و راه‌حل‌ها

۱. خطاهای CORS

// Error: Access to fetch at 'https://api.example.com' from origin 'http://localhost:3000' has been blocked by CORS policy

// Solution: Server must send appropriate CORS headers
// Or use proxy in development
const response = await fetch("/api/users"); // Relative URL

۲. ۴۰۴ Not Found

// Check URL in Network tab
// Verify endpoint exists
// Check for typos

۳. ۴۰۱ Unauthorized

// Missing or invalid authentication
const response = await fetch("/api/protected", {
  headers: {
    Authorization: "Bearer " + token,
  },
});

۴. ۵۰۰ Server Error

// Server-side error
// Check server logs
// Verify request format

چک‌لیست دیباگ

// 1. Check Network tab
// - Is request being sent?
// - What's the status code?
// - Are headers correct?

// 2. Check Console for errors
// - JavaScript errors
// - Network errors

// 3. Verify request format
// - Correct URL
// - Appropriate headers
// - Valid body data

// 4. Test with tools
// - Postman
// - curl
// - Browser Network tab

🌍 مثال‌های API دنیای واقعی

API آب و هوا

class WeatherAPI {
  constructor(apiKey) {
    this.apiKey = apiKey;
    this.baseUrl = "https://api.openweathermap.org/data/2.5";
  }

  async getCurrentWeather(city) {
    try {
      const response = await fetch(
        `${this.baseUrl}/weather?q=${encodeURIComponent(
          city
        )}&units=metric&appid=${this.apiKey}`
      );

      if (!response.ok) {
        throw new Error(`City not found: ${city}`);
      }

      return await response.json();
    } catch (error) {
      console.error("Failed to fetch weather:", error);
      throw error;
    }
  }

  async getForecast(city) {
    const response = await fetch(
      `${this.baseUrl}/forecast?q=${encodeURIComponent(
        city
      )}&units=metric&appid=${this.apiKey}`
    );

    if (!response.ok) {
      throw new Error(`Forecast not available for ${city}`);
    }

    return await response.json();
  }
}

// Usage
const weather = new WeatherAPI("your-api-key");
weather
  .getCurrentWeather("Tehran")
  .then((data) => console.log("Weather:", data))
  .catch((error) => console.error("Error:", error));

API گیت‌هاب

class GitHubAPI {
  constructor(token = null) {
    this.baseUrl = "https://api.github.com";
    this.headers = {
      Accept: "application/vnd.github.v3+json",
    };

    if (token) {
      this.headers["Authorization"] = `token ${token}`;
    }
  }

  async getUser(username) {
    const response = await fetch(`${this.baseUrl}/users/${username}`, {
      headers: this.headers,
    });

    if (!response.ok) {
      throw new Error(`User not found: ${username}`);
    }

    return await response.json();
  }

  async getUserRepos(username) {
    const response = await fetch(`${this.baseUrl}/users/${username}/repos`, {
      headers: this.headers,
    });

    if (!response.ok) {
      throw new Error(`Failed to fetch repositories for ${username}`);
    }

    return await response.json();
  }
}

// Usage
const github = new GitHubAPI();
github
  .getUser("octocat")
  .then((user) => console.log("User:", user))
  .catch((error) => console.error("Error:", error));

🎯 تمرینات عملی

تمرین ۱: Fetch پایه

// Create a function that fetches and displays user information
async function displayUser(userId) {
  try {
    const response = await fetch(
      `https://jsonplaceholder.typicode.com/users/${userId}`
    );

    if (!response.ok) {
      throw new Error(`User not found: ${userId}`);
    }

    const user = await response.json();

    // Display user information
    document.getElementById("user-info").innerHTML = `
            <h2>${user.name}</h2>
            <p>Email: ${user.email}</p>
            <p>Phone: ${user.phone}</p>
            <p>Company: ${user.company.name}</p>
        `;
  } catch (error) {
    console.error("Error:", error);
    document.getElementById(
      "user-info"
    ).innerHTML = `<p style="color: red;">Error: ${error.message}</p>`;
  }
}

// Test it
displayUser(1);

تمرین ۲: حالت‌های بارگذاری

// Create a function that shows loading state during fetch
async function fetchWithLoading(url, displayElement) {
  // Show loading
  displayElement.innerHTML = '<div class="loading">Loading...</div>';

  try {
    const response = await fetch(url);

    if (!response.ok) {
      throw new Error(`HTTP ${response.status}`);
    }

    const data = await response.json();

    // Display data
    displayElement.innerHTML = `<pre>${JSON.stringify(data, null, 2)}</pre>`;
  } catch (error) {
    // Display error
    displayElement.innerHTML = `<div class="error">Error: ${error.message}</div>`;
  }
}

// Usage
fetchWithLoading(
  "https://jsonplaceholder.typicode.com/posts/1",
  document.getElementById("result")
);

تمرین ۳: درخواست‌های متعدد

// Fetch user and their posts simultaneously
async function fetchUserAndPosts(userId) {
  try {
    const [userResponse, postsResponse] = await Promise.all([
      fetch(`https://jsonplaceholder.typicode.com/users/${userId}`),
      fetch(`https://jsonplaceholder.typicode.com/posts?userId=${userId}`),
    ]);

    if (!userResponse.ok || !postsResponse.ok) {
      throw new Error("Failed to fetch data");
    }

    const [user, posts] = await Promise.all([
      userResponse.json(),
      postsResponse.json(),
    ]);

    console.log("User:", user);
    console.log("Posts:", posts);

    return { user, posts };
  } catch (error) {
    console.error("Error:", error);
    throw error;
  }
}

// Usage
fetchUserAndPosts(1).then((data) => {
  console.log("Complete data:", data);
});

🚨 بهترین شیوه‌های مدیریت خطا

مدیریت خطای جامع

class APIClient {
  constructor(baseUrl) {
    this.baseUrl = baseUrl;
  }

  async request(endpoint, options = {}) {
    try {
      const url = `${this.baseUrl}${endpoint}`;
      const response = await fetch(url, {
        headers: {
          "Content-Type": "application/json",
          ...options.headers,
        },
        ...options,
      });

      // Handle different status codes
      if (response.status === 401) {
        throw new Error("Unauthorized - Please log in");
      } else if (response.status === 403) {
        throw new Error("Forbidden - You don't have permission");
      } else if (response.status === 404) {
        throw new Error("Resource not found");
      } else if (response.status === 429) {
        throw new Error("Too many requests - Please wait");
      } else if (response.status >= 500) {
        throw new Error("Server error - Please try again later");
      } else if (!response.ok) {
        throw new Error(`HTTP ${response.status}: ${response.statusText}`);
      }

      // Parse response based on content type
      const contentType = response.headers.get("content-type");

      if (contentType && contentType.includes("application/json")) {
        return await response.json();
      } else {
        return await response.text();
      }
    } catch (error) {
      // Log error for debugging
      console.error("API request failed:", {
        endpoint,
        error: error.message,
        timestamp: new Date().toISOString(),
      });

      // Re-throw with context
      throw new Error(`API request failed: ${error.message}`);
    }
  }

  async get(endpoint) {
    return this.request(endpoint, { method: "GET" });
  }

  async post(endpoint, data) {
    return this.request(endpoint, {
      method: "POST",
      body: JSON.stringify(data),
    });
  }

  async put(endpoint, data) {
    return this.request(endpoint, {
      method: "PUT",
      body: JSON.stringify(data),
    });
  }

  async delete(endpoint) {
    return this.request(endpoint, { method: "DELETE" });
  }
}

// Usage
const api = new APIClient("https://jsonplaceholder.typicode.com");

try {
  const user = await api.get("/users/1");
  console.log("User:", user);
} catch (error) {
  console.error("Failed to fetch user:", error.message);
}

🎓 خلاصه

شما یاد گرفتید چگونه:

درخواست‌های HTTP را با استفاده از Fetch API ارسال کنید ✅ انواع مختلف پاسخ (JSON، متن، فایل‌ها) را مدیریت کنید ✅ مشکلات شبکه را با Chrome DevTools دیباگ کنید ✅ مدیریت خطا را برای اپلیکیشن‌های قوی پیاده‌سازی کنید ✅ با API های واقعی کار کنید و ساختار آنها را درک کنید ✅ درخواست‌ها را با تایم‌اوت و منطق تلاش مجدد بهینه کنید

مراحل بعدی

  1. تمرین با API های واقعی: API های عمومی مختلف را امتحان کنید
  2. ساخت پروژه‌ها: اپلیکیشن‌هایی بسازید که از چندین API استفاده می‌کنند
  3. یادگیری احراز هویت: OAuth، توکن‌های JWT، کلیدهای API
  4. کاوش موضوعات پیشرفته: WebSockets، Server-Sent Events
  5. مطالعه عمیق HTTP: هدرها، کش، امنیت

منابع مفید

یادتان باشد: دیباگ شبکه مهارت مهمی برای هر توسعه‌دهنده فرانت‌اند است. هرچه بیشتر تمرین کنید، بهتر در شناسایی و حل مشکلات مربوط به شبکه خواهید شد! 🚀

On this page

🌐 مقدمه‌ای بر ارتباط شبکهآنچه یاد خواهید گرفتچرا این موضوع مهم است؟📡 درک درخواست‌های HTTPHTTP چیست؟ساختار پایه درخواست HTTPمتدهای HTTPکدهای وضعیت HTTP🚀 مبانی Fetch APIدرخواست GET سادهدرک زنجیره Promise در Fetchنسخه Async/Await (تمیزتر)📤 ارسال انواع مختلف درخواست‌هادرخواست GET (دریافت داده)درخواست POST (ایجاد داده)درخواست PUT (به‌روزرسانی کامل منبع)درخواست DELETE (حذف داده)آپلود فایل‌ها🔧 ویژگی‌های پیشرفته Fetchگزینه‌های درخواستمدیریت انواع مختلف پاسخپیاده‌سازی تایم‌اوتمنطق تلاش مجدد🛠️ تب Network در Chrome DevToolsباز کردن تب Networkدرک تب Networkلیست درخواست‌هاجزئیات درخواستفیلتر کردن درخواست‌هاویژگی‌های تب Network۱. حفظ لاگ۲. غیرفعال کردن کش۳. محدود کردن سرعت۴. مسدود کردن درخواست🔍 دیباگ درخواست‌های شبکهمشکلات رایج و راه‌حل‌ها۱. خطاهای CORS۲. ۴۰۴ Not Found۳. ۴۰۱ Unauthorized۴. ۵۰۰ Server Errorچک‌لیست دیباگ🌍 مثال‌های API دنیای واقعیAPI آب و هواAPI گیت‌هاب🎯 تمرینات عملیتمرین ۱: Fetch پایهتمرین ۲: حالت‌های بارگذاریتمرین ۳: درخواست‌های متعدد🚨 بهترین شیوه‌های مدیریت خطامدیریت خطای جامع🎓 خلاصهمراحل بعدیمنابع مفید