آموزش جامع Axios
HTTP Client قدرتمند Promise-based برای مرورگر و Node.js — جایگزینی بهتر از Fetch.
📦معرفی و مقایسه با Fetch
Axios یک کتابخانه Promise-based برای ارسال درخواستهای HTTP است که هم در مرورگر (بر پایه XMLHttpRequest) و هم در Node.js (بر پایه http module) کار میکند.
Axios در مقابل Fetch API
| ویژگی | Axios | Fetch API |
|---|---|---|
| Parse JSON | خودکار | نیاز به .json() |
| مدیریت خطای HTTP | 4xx/5xx → reject | فقط خطای شبکه |
| Timeout | داخلی | نیاز به AbortController |
| Interceptors | دارد | ندارد |
| Upload Progress | onUploadProgress | ندارد |
| XSRF Protection | داخلی | دستی |
| Request/Response Transform | دارد | ندارد |
| Node.js Support | بله | Node 18+ |
| حجم | ~۳۲KB | داخلی مرورگر |
نصب
<!-- روش ۱: فایل لوکال --> <script src="axios.min.js"></script> <!-- روش ۲: CDN --> <script src="https://cdn.jsdelivr.net/npm/axios@1.6.8/dist/axios.min.js"></script> /* روش ۳: NPM */ // npm install axios import axios from 'axios';
ساختار Response
// هر پاسخ موفق Axios یک آبجکت با این فیلدها دارد: { data: {}, // ← JSON بهصورت خودکار parse شده status: 200, // کد HTTP statusText: 'OK', // متن وضعیت headers: {}, // هدرهای پاسخ config: {}, // تنظیمات درخواست request: {} // آبجکت XMLHttpRequest }
متدهای اصلی
| متد | HTTP | کاربرد |
|---|---|---|
axios.get(url, config?) | GET | دریافت داده |
axios.post(url, data, config?) | POST | ایجاد رکورد جدید |
axios.put(url, data, config?) | PUT | جایگزینی کامل |
axios.patch(url, data, config?) | PATCH | بروزرسانی جزئی |
axios.delete(url, config?) | DELETE | حذف رکورد |
axios(config) | همه | درخواست کاملاً سفارشی |
jsonplaceholder.typicode.com) استفاده میکنند. برای اجرای دموها به اینترنت نیاز دارید.
📥درخواست GET GET
GET برای دریافت داده از سرور استفاده میشود. پارامترهای query string را با params پاس دهید.
GET پایه
// Promise chain axios.get('https://jsonplaceholder.typicode.com/posts/1') .then(res => { console.log(res.data); // JSON parse شده console.log(res.status); // 200 }) .catch(err => console.error(err.message));
GET با Query Parameters
// Axios بهصورت خودکار params را به ?key=val تبدیل میکند axios.get('https://jsonplaceholder.typicode.com/posts', { params: { userId: 1, // ?userId=1 _limit: 5 // &_limit=5 } }).then(res => console.log(res.data)); // URL نهایی: /posts?userId=1&_limit=5
درخواستهای موازی
// Promise.all — همه موازی اجرا میشوند const [postRes, userRes] = await Promise.all([ axios.get('/posts/1'), axios.get('/users/1') ]); console.log(postRes.data, userRes.data); // axios.all + axios.spread (روش قدیمیتر) axios.all([req1, req2]) .then(axios.spread((res1, res2) => { /* ... */ }));
📤POST / PUT / PATCH / DELETE POST PUT PATCH DELETE
POST — ایجاد رکورد
Axios بهصورت خودکار object را به JSON تبدیل و هدر Content-Type: application/json را تنظیم میکند:
axios.post('https://jsonplaceholder.typicode.com/posts', { title: 'عنوان پست جدید', body: 'متن کامل پست', userId: 1 }).then(res => { console.log(res.status); // 201 Created console.log(res.data.id); // ID رکورد جدید });
PUT و PATCH
// PUT: جایگزینی کامل رکورد axios.put('/posts/1', { id: 1, title: 'عنوان جدید', body: 'متن جدید', userId: 1 }); // PATCH: فقط فیلدهای مشخص شده آپدیت میشوند axios.patch('/posts/1', { title: 'فقط عنوان تغییر میکند' });
DELETE
axios.delete('/posts/1') .then(res => console.log('حذف شد — status:', res.status)); // 200
درخواست سفارشی با config کامل
// روش عمومی — برای کنترل کامل axios({ method: 'post', url: '/api/posts', data: { title: '...' }, headers: { 'Authorization': 'Bearer token' }, timeout: 5000, params: { source: 'web' } });
🔐هدرها، احراز هویت و Timeout
هدرهای یک درخواست
axios.get('/api/profile', { headers: { 'Authorization': `Bearer ${token}`, 'Accept-Language': 'fa', 'X-Request-ID': 'uuid-1234' } });
هدرهای سراسری
// برای همه درخواستها axios.defaults.headers.common['Authorization'] = 'Bearer ' + token; axios.defaults.baseURL = 'https://api.mysite.com/v1'; axios.defaults.timeout = 10000; // فقط برای POST/PUT axios.defaults.headers.post['Content-Type'] = 'application/json';
Timeout
axios.get('/api/slow', { timeout: 5000 }) // ۵ ثانیه .catch(err => { if (err.code === 'ECONNABORTED') { console.log('⏱ درخواست به timeout رسید'); } });
Basic Authentication
// روش ۱: با auth object axios.get('/api/data', { auth: { username: 'user', password: 'pass' } }); // هدر Authorization: Basic dXNlcjpwYXNz را اضافه میکند // روش ۲: توکن JWT axios.get('/api/data', { headers: { 'Authorization': `Bearer ${localStorage.getItem('token')}` } });
🔄Interceptorها
Interceptorها یکی از قدرتمندترین قابلیتهای Axios هستند. کد را قبل از ارسال درخواست یا بعد از دریافت پاسخ اجرا میکنند — بدون تکرار در هر call.
Request و Response Interceptor
// ── Interceptor درخواست ── axios.interceptors.request.use( config => { // قبل از ارسال هر درخواست const token = localStorage.getItem('token'); if (token) config.headers.Authorization = `Bearer ${token}`; config.metadata = { startTime: new Date() }; return config; // ← حتماً config را برگردانید }, error => Promise.reject(error) ); // ── Interceptor پاسخ ── axios.interceptors.response.use( response => { // هر پاسخ موفق (2xx) const elapsed = new Date() - response.config.metadata?.startTime; console.log(`✅ ${response.status} — ${elapsed}ms`); return response; }, error => { // هر پاسخ خطا if (error.response?.status === 401) { // توکن منقضی — هدایت به صفحه ورود window.location.href = '/login'; } if (error.response?.status === 429) { // Rate limit — میتوان retry کرد console.log('⚠️ Rate limit — دوباره تلاش کنید'); } return Promise.reject(error); } );
حذف Interceptor
const id = axios.interceptors.request.use(config => config); axios.interceptors.request.eject(id); // حذف
Retry Interceptor — تلاش مجدد خودکار
// تلاش مجدد خودکار در صورت خطای شبکه یا ۵xx axios.interceptors.response.use(null, async error => { const config = error.config; if (!config || config._retryCount >= 3) return Promise.reject(error); const shouldRetry = !error.response || // خطای شبکه error.response.status >= 500; // خطای سرور if (!shouldRetry) return Promise.reject(error); config._retryCount = (config._retryCount || 0) + 1; const delay = 1000 * config._retryCount; // 1s, 2s, 3s await new Promise(r => setTimeout(r, delay)); console.log(`🔄 تلاش ${config._retryCount}/3...`); return axios(config); });
⚠مدیریت خطاها
Axios هر کد HTTP خارج از محدوده 2xx را بهعنوان خطا reject میکند. آبجکت خطا سه ساختار مختلف دارد:
try { await axios.get('/api/data'); } catch (error) { if (error.response) { // سرور پاسخ داد — کد خطا (4xx یا 5xx) console.log(error.response.status); // 404, 403, 500 ... console.log(error.response.data); // body پاسخ خطا console.log(error.response.headers); // هدرهای پاسخ } else if (error.request) { // درخواست ارسال شد ولی پاسخ نرسید (مشکل شبکه / CORS) console.log('Network Error', error.request); } else { // خطا در ساختن درخواست console.log('Config Error:', error.message); } // بررسی timeout if (error.code === 'ECONNABORTED') console.log('⏱ Timeout!'); // بررسی cancel if (axios.isCancel(error)) console.log('درخواست لغو شد'); }
جدول خطاها
| خطا | دلیل | شناسایی |
|---|---|---|
| 400 Bad Request | داده نامعتبر | status === 400 |
| 401 Unauthorized | توکن نامعتبر/منقضی | status === 401 |
| 403 Forbidden | عدم دسترسی | status === 403 |
| 404 Not Found | منبع پیدا نشد | status === 404 |
| 422 Unprocessable | Validation Error | status === 422 |
| 429 Too Many Requests | Rate Limit | status === 429 |
| 500+ Server Error | خطای داخلی سرور | status >= 500 |
| Network Error | قطعی شبکه / CORS | !error.response |
| Timeout | زمان انتظار تمام شد | error.code === 'ECONNABORTED' |
| Cancelled | لغو توسط برنامه | axios.isCancel(error) |
⚡async/await و axios.create
الگوی async/await
async function getUser(id) { try { const { data } = await axios.get(`/users/${id}`); // ← destructuring مستقیم data return data; } catch (error) { if (error.response?.status === 404) return null; throw error; } } // await روی چند درخواست موازی const [users, posts] = await Promise.all([ axios.get('/users').then(r => r.data), axios.get('/posts').then(r => r.data), ]);
axios.create() — Instance اختصاصی
// ── api.js ── const api = axios.create({ baseURL: 'https://api.mysite.com/v1', timeout: 10000, headers: { 'Content-Type': 'application/json', 'X-App-Version': '2.0.0' } }); // Interceptor روی instance اختصاصی api.interceptors.request.use(config => { config.headers.Authorization = `Bearer ${getToken()}`; return config; }); // استفاده در همه جا — بدون تکرار تنظیمات api.get('/users'); api.post('/posts', data); api.delete(`/comments/${id}`);
📤آپلود فایل با Progress
Axios از طریق onUploadProgress و onDownloadProgress امکان نمایش پیشرفت آپلود/دانلود را فراهم میکند. این ویژگی در Fetch API وجود ندارد.
آپلود فایل
const formData = new FormData(); formData.append('file', fileInput.files[0]); formData.append('name', 'profile-photo'); axios.post('/api/upload', formData, { headers: { 'Content-Type': 'multipart/form-data' }, onUploadProgress: progressEvent => { const percent = Math.round( (progressEvent.loaded / progressEvent.total) * 100 ); progressBar.style.width = percent + '%'; label.textContent = percent + '% آپلود شد'; } });
دانلود با Progress
axios.get('/api/large-file', { responseType: 'blob', onDownloadProgress: e => { if (e.total) { const pct = Math.round((e.loaded / e.total) * 100); progressBar.style.width = pct + '%'; } } }).then(res => { // ذخیره فایل دانلود شده const url = URL.createObjectURL(res.data); const link = document.createElement('a'); link.href = url; link.download = 'file.pdf'; link.click(); });
✖لغو درخواست (Cancel Request)
گاهی نیاز دارید یک درخواست در حال اجرا را لغو کنید — مثلاً وقتی کاربر از صفحه خارج میشود یا یک جستجوی جدید میزند. Axios از AbortController پشتیبانی میکند.
روش AbortController (مدرن — توصیهشده)
const controller = new AbortController(); // ارسال درخواست با signal axios.get('/api/data', { signal: controller.signal }) .then(res => console.log(res.data)) .catch(err => { if (axios.isCancel(err)) { console.log('درخواست لغو شد:', err.message); } }); // لغو (هر زمان) controller.abort('کاربر از صفحه خارج شد');
الگوی لغو در جستجو (Search Debounce)
let controller; searchInput.addEventListener('input', async (e) => { // لغو درخواست قبلی قبل از ارسال جدید if (controller) controller.abort(); controller = new AbortController(); try { const { data } = await axios.get('/api/search', { params: { q: e.target.value }, signal: controller.signal }); renderResults(data); } catch (err) { if (!axios.isCancel(err)) console.error(err); // خطاهای cancel را نادیده میگیریم } });
لغو در React useEffect
useEffect(() => {
const controller = new AbortController();
axios.get(`/api/user/${userId}`, { signal: controller.signal })
.then(res => setUser(res.data))
.catch(err => { if (!axios.isCancel(err)) setError(err); });
return () => controller.abort(); // ← cleanup هنگام unmount
}, [userId]);
🌟معماری سرویس واقعی
در پروژههای واقعی، درخواستهای HTTP را در فایلهای سرویس جداگانه سازماندهی کنید. این الگو کد را قابل نگهداری، تستپذیر و DRY نگه میدارد.
فایل api.js — پایه
// src/api/api.js const api = axios.create({ baseURL: process.env.REACT_APP_API_URL || 'https://api.mysite.com/v1', timeout: 15000, headers: { 'Content-Type': 'application/json' } }); // افزودن توکن به همه درخواستها api.interceptors.request.use(config => { const token = localStorage.getItem('access_token'); if (token) config.headers.Authorization = `Bearer ${token}`; return config; }); // مدیریت خطا و refresh توکن api.interceptors.response.use( res => res, async err => { if (err.response?.status === 401 && !err.config._retry) { err.config._retry = true; try { const { data } = await axios.post('/auth/refresh', { refreshToken: localStorage.getItem('refresh_token') }); localStorage.setItem('access_token', data.token); err.config.headers.Authorization = `Bearer ${data.token}`; return api(err.config); // ← retry با توکن جدید } catch { window.location.href = '/login'; } } return Promise.reject(err); } ); export default api;
سرویسهای اختصاصی
// src/services/userService.js import api from '../api/api'; export const userService = { getAll: (params = {}) => api.get('/users', { params }), getById: id => api.get(`/users/${id}`), create: data => api.post('/users', data), update: (id, data) => api.put(`/users/${id}`, data), patch: (id, data) => api.patch(`/users/${id}`, data), remove: id => api.delete(`/users/${id}`), upload: (id, file, onProgress) => { const fd = new FormData(); fd.append('avatar', file); return api.post(`/users/${id}/avatar`, fd, { headers: { 'Content-Type': 'multipart/form-data' }, onUploadProgress: e => onProgress?.(Math.round(e.loaded/e.total*100)) }); } }; // استفاده در component const users = await userService.getAll({ page: 1, limit: 20 }); await userService.update(5, { name: 'نام جدید' });
- یک
api.jsباaxios.create()بسازید - توکن را در request interceptor اضافه کنید
- refresh توکن را در response interceptor پیاده کنید
- برای هر منبع یک فایل سرویس جداگانه بسازید
- AbortController را در componentهای React cleanup کنید
- در محیط تولید،
timeoutمعقول تنظیم کنید
🚀زمین بازی کد (Playground)
کد Axios را همینجا اجرا کن و خروجی را در کنسول پایین ببین. چون محیط آفلاین است، یک adapter ساختگی پاسخ سرور را شبیهسازی میکند تا روی الگوی واقعی Axios (GET/POST، async/await، Interceptor) تمرین کنی.
console.log() در پنل پایینِ پیشنمایش نمایش داده میشود. نسخهٔ ۱.۶.۸ همین صفحه آفلاین بارگذاری میشود.