کار با API های خارجی در Node.js
دسترسی به APIهای خارجی یکی از نیازهای رایج در توسعهٔ بکاند است. در Node.js میتوانید با REST، GraphQL یا سرویسهای مبتنی بر WebSocket ارتباط برقرار کنید. این راهنما نکات عملی، الگوهای امن و نمونههای کد واقعی را پوشش میدهد تا بتوانید با اطمینان عملکرد و مقیاسپذیری را افزایش دهید.
مفاهیم پایه و بهترین شیوهها
- استفاده از env برای کلید و توکنها (مثلاً با dotenv).
- تنظیم timeout و abort برای جلوگیری از درخواستهای آویزان.
- پیادهسازی retry با backoff نمایی برای مقابله با خطاهای موقت.
- کشینگ (in-memory یا Redis) برای کاهش بار روی APIهای خارجی و بهبود تاخیر.
- رعایت نرخ درخواست (rate limiting) و مدیریت کوتاهمدت (circuit breaker).
مقایسهٔ سریع: fetch vs axios
| ویژگی | node-fetch / native fetch | axios |
|---|---|---|
| پشتیبانی از JSON | res.json() ساده | خودکار، به صورت پیشفرض |
| Timeout | با AbortController | پارامتر timeout ساده |
| Intercept / Retry | نیاز به کد دستی | interceptors و کتابخانههای کمکی |
مثال ۱: درخواست ساده با native fetch و مدیریت Abort
// node 18+ native fetch
import fetch from 'node-fetch'; // if using older node
import AbortController from 'abort-controller';
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 5000);
try {
const res = await fetch('https://api.example.com/data', {
method: 'GET',
headers: { 'Accept': 'application/json' },
signal: controller.signal
});
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const data = await res.json();
console.log(data);
} catch (err) {
if (err.name === 'AbortError') console.error('Request timed out');
else console.error('Fetch error', err);
} finally {
clearTimeout(timeout);
}توضیح: این کد یک درخواست GET با native fetch انجام میدهد و با AbortController پس از ۵ ثانیه درخواست را قطع میکند. چک کردن res.ok کمک میکند خطاهای HTTP (مثل ۴xx/۵xx) را مدیریت کنیم. این روش از آویزان شدن پروسس جلوگیری میکند.
مثال ۲: axios با retry و backoff (بهینهسازی)
import axios from 'axios';
import axiosRetry from 'axios-retry';
const client = axios.create({
baseURL: 'https://api.example.com',
timeout: 7000, // milliseconds
headers: { 'Authorization': `Bearer ${process.env.API_TOKEN}` }
});
axiosRetry(client, {
retries: 3,
retryDelay: (retryCount) => Math.pow(2, retryCount) * 100, // exponential backoff
retryCondition: (error) => {
return axiosRetry.isNetworkOrIdempotentRequestError(error) || error.response?.status >= 500;
}
});
try {
const resp = await client.get('/resource');
console.log(resp.data);
} catch (err) {
console.error('API failed after retries', err.message);
}توضیح: در این مثال از axios و axios-retry استفاده کردیم. با ایجاد یک client میتوان هدرها، تایماوت و baseURL را مرکزی تعریف کرد. تنظیم retryDelay نمایی باعث کاهش فشار روی سرویس میشود و فقط برای خطاهای شبکه یا خطاهای سرور (۵xx) تلاش مجدد میشود.
بهبود: اضافه کردن circuit breaker
import CircuitBreaker from 'opossum';
const breakerOptions = {
timeout: 8000,
errorThresholdPercentage: 50,
resetTimeout: 30000
};
const wrapped = new CircuitBreaker((url) => client.get(url), breakerOptions);
wrapped.fallback(() => ({ data: null, warning: 'Service unavailable' }));
try {
const { data } = await wrapped.fire('/resource');
console.log(data);
} catch (err) {
console.error('Circuit open or failed', err);
}توضیح: استفاده از circuit breaker (مثلاً opossum) کمک میکند وقتی سرویس خارجی دچار مشکل شد، درخواستهای بعدی سریعاً رد شوند یا به fallback بروند و فشار روی سیستم و سرویس خارجی کاهش یابد.
کشینگ و کاهش درخواستها
اگر دادهها تکرارشونده یا با فرکانس تغییر پایین هستند، از کش استفاده کنید. مثال با Redis برای مقیاسپذیری:
import Redis from 'ioredis';
const redis = new Redis(process.env.REDIS_URL);
async function getCached(path) {
const cacheKey = `api:${path}`;
const cached = await redis.get(cacheKey);
if (cached) return JSON.parse(cached);
const resp = await client.get(path);
await redis.set(cacheKey, JSON.stringify(resp.data), 'EX', 60); // expires in 60s
return resp.data;
}توضیح: این تابع ابتدا کش را بررسی میکند و در صورت نبود، از API میگیرد، سپس در Redis با TTL مشخص ذخیره میکند. این الگو هزینه و تاخیر را کاهش میدهد و برای دادههای قابل قبول eventual consistency مناسب است.
امنیت، rate limiting و نکات حقوقی
- هرگز کلیدها را در سورس کنترل نگذارید؛ از env یا secret management استفاده کنید.
- اگر API محدودیت نرخ دارد، از صفبندی، backoff و sleep بین درخواستها استفاده کنید.
- برای OAuth، refresh token را امن نگه دارید و گردش (rotate) توکن را پیاده کنید.
- در قرارداد با سرویسدهنده، محدودیتهای نرخ و SLA را بررسی کنید تا طراحی سیستم با آنها سازگار شود.
اشکالزدایی و مانیتورینگ
برای سرویسهای حیاتی متریکهایی مثل نرخ خطا، تاخیر (p95)، تعداد retries و وضعیت circuit breaker را ثبت و مانیتور کنید. لاگهای ساختاریافته و tracing (مثل OpenTelemetry) به تشخیص مشکل در زنجیره درخواست کمک میکند.
جمعبندی
کار با APIهای خارجی در Node.js فقط شامل ارسال درخواست نیست؛ مدیریت خطا، محدودیتها، امنیت و کشینگ بسیار مهماند. انتخاب بین fetch و axios بستگی به نیاز به interceptors و retry دارد. استفاده از retry با backoff، circuit breaker و کش مناسب کمک میکند سیستم قابل اعتماد، مقیاسپذیر و امن بسازید.
در صورت نیاز میتوانم نمونهٔ پروژهٔ کوچک با Express و middlewareهای مشخص برای retry، cache و rate-limit آماده کنم.
آیا این مطلب برای شما مفید بود ؟




