آموزش 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
- Chrome DevTools را باز کنید (F12 یا Ctrl+Shift+I)
- روی تب Network کلیک کنید
- صفحه را رفرش کنید یا عملی را تحریک کنید
درک تب 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 های واقعی کار کنید و ساختار آنها را درک کنید ✅ درخواستها را با تایماوت و منطق تلاش مجدد بهینه کنید
مراحل بعدی
- تمرین با API های واقعی: API های عمومی مختلف را امتحان کنید
- ساخت پروژهها: اپلیکیشنهایی بسازید که از چندین API استفاده میکنند
- یادگیری احراز هویت: OAuth، توکنهای JWT، کلیدهای API
- کاوش موضوعات پیشرفته: WebSockets، Server-Sent Events
- مطالعه عمیق HTTP: هدرها، کش، امنیت
منابع مفید
- API های عمومی: JSONPlaceholder، Nikode Api
- کدهای وضعیت HTTP: MDN HTTP Status
- Fetch API: MDN Fetch
- Chrome DevTools: راهنمای تب Network
یادتان باشد: دیباگ شبکه مهارت مهمی برای هر توسعهدهنده فرانتاند است. هرچه بیشتر تمرین کنید، بهتر در شناسایی و حل مشکلات مربوط به شبکه خواهید شد! 🚀