“We’ve all been there. Your Node.js app talks to a payment gateway or third-party API, and everything works… until it doesn’t. One network hiccup, and your app fails hard.”
Real Problem:
- External APIs might:
- Rate-limit you
- Drop connections randomly
- Throw 500s under load
- A retry mechanism can prevent unnecessary app failures.
💥 Naive Retry Example (Don’t Do This)
function fetchWithRetry(url, attempts = 3) {
return fetch(url).catch(err => {
if (attempts <= 1) throw err;
return fetchWithRetry(url, attempts - 1);
});
}
What’s wrong here?
- No delay between retries.
- Immediate hammering worsens server load.
- No distinction between fatal vs retryable errors.
🧠 Retry Best Practices
🔧 Using axios-retry (Easiest)
npm install axios axios-retry
const axios = require('axios');
const axiosRetry = require('axios-retry');
axiosRetry(axios, {
retries: 3,
retryDelay: axiosRetry.exponentialDelay,
retryCondition: (error) => {
return error.response?.status >= 500 || error.code === 'ECONNABORTED';
}
});
axios.get('https://flaky.api.com/data')
.then(res => console.log(res.data))
.catch(err => console.error('Request failed after retries', err));
🛠 Using p-retry for Custom Logic
npm install p-retry
const pRetry = require('p-retry');
const fetch = require('node-fetch');
const fetchData = async () => {
const res = await fetch('https://unstable-api.com/data');
if (!res.ok) throw new Error(`Status: ${res.status}`);
return res.json();
};
pRetry(fetchData, {
retries: 5,
onFailedAttempt: error => {
console.log(`Attempt ${error.attemptNumber} failed. There are ${error.retriesLeft} retries left.`);
}
}).then(console.log).catch(console.error);
⚠️ Retry-Aware Design
- Always use idempotent methods (GET, PUT) for retries.
- POST requests can cause duplicate side effects if retried without safeguards (e.g., charge twice).
💬 Logging and Monitoring
onFailedAttempt: error => {
log.warn({
attempt: error.attemptNumber,
message: error.message
});
}
- Helps in debugging production failures.
📦 Bonus: Retry with Queues (Bull/Agenda)
For longer retry flows:
- Use bull (Redis-based job queue) to retry in background.
- Useful for payment retries, webhook delivery, etc.
🔚 Conclusion
“Retries are like seatbelts for your API calls. You hope you don’t need them — but when you do, they can save your system from disaster.”
Encourage readers to:
- Use libraries, not reinvent the wheel.
- Always handle retries thoughtfully , not blindly.
Optional Enhancements
- Retry + Circuit Breaker (use opossum)
- Retry + Metrics for success/failure tracking
- Custom retry decorator function
I’m Sachin Kasana — Principal Engineer, full-stack enthusiast, and open-source contributor. I write practical dev blogs to make backend scaling, performance, and clean code simpler for everyone.
📬 Enjoyed this post?
Follow me on Medium for more hands-on backend, Node.js, and architecture articles every week.
Top comments (0)