Retry a promise-returning or async function
p-retry is a battle-tested npm package that wraps promise-returning or async functions with automatic retry logic and exponential backoff. When network requests fail, APIs are temporarily unavailable, or rate limits are hit, p-retry systematically retries operations with increasing delays between attempts, preventing service overload while maximizing success rates.
With over 24 million weekly downloads, p-retry has become the de facto standard for handling transient failures in Node.js applications. It intelligently distinguishes between retriable errors (like network timeouts) and permanent failures (like 404s), avoiding wasteful retries on operations that will never succeed. The package defaults to 10 retry attempts but exposes granular control through hooks like onFailedAttempt and shouldRetry.
Built by Sindre Sorhus and maintained with zero side effects, p-retry requires Node.js 20+ and integrates seamlessly with modern async/await patterns. It leverages the is-network-error dependency to automatically detect network-related failures while skipping retries for programming errors like TypeErrors. The exponential backoff algorithm progressively increases delays between attempts, making it ideal for microservices, external API integrations, and database connection handling.
Developers choose p-retry when they need production-grade reliability without building custom retry infrastructure. Its simple API wraps existing async functions with minimal refactoring, while the TypeScript definitions and comprehensive error context (attempt number, retriesLeft, elapsed time) enable sophisticated failure handling and observability.
import pRetry, { AbortError } from 'p-retry';
const fetchUserData = async (userId) => {
const response = await fetch(`https://api.example.com/users/${userId}`);
if (response.status === 404) {
throw new AbortError('User not found');
}
if (response.status === 429) {
const retryAfter = response.headers.get('Retry-After');
const error = new Error('Rate limited');
error.retryAfter = retryAfter ? parseInt(retryAfter) * 1000 : 5000;
throw error;
}
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
return response.json();
};
const result = await pRetry(() => fetchUserData('user-123'), {
retries: 5,
minTimeout: 1000,
maxTimeout: 10000,
onFailedAttempt: async (error) => {
console.log(`Attempt ${error.attemptNumber} failed. ${error.retriesLeft} retries left.`);
console.log(`Error: ${error.message}`);
if (error.retryAfter) {
console.log(`Waiting ${error.retryAfter}ms before retry due to rate limit`);
await new Promise(resolve => setTimeout(resolve, error.retryAfter));
}
},
shouldRetry: (error) => {
if (error.message.includes('ENOTFOUND')) return true;
if (error.name === 'TimeoutError') return true;
return error.message.includes('HTTP 5');
}
});
console.log('User data:', result);External API Integration: Retry HTTP requests to third-party APIs that experience intermittent downtime or rate limiting. The exponential backoff prevents hammering the service while shouldRetry can inspect HTTP status codes to abort on client errors (4xx) but retry on server errors (5xx).
Database Connection Pools: Wrap database connection logic to handle transient failures during peak load or network hiccups. The onFailedAttempt hook can log connection failures to monitoring systems before retrying, providing visibility into infrastructure health.
Distributed Job Processing: Retry failed background jobs in worker queues (Bull, BullMQ) where transient errors like Redis connection drops shouldn't permanently fail tasks. Set custom maxRetryTime to ensure jobs don't retry indefinitely and block queue throughput.
File System Operations: Handle EMFILE (too many open files) and EBUSY errors when reading/writing files in high-concurrency scenarios. p-retry automatically backs off to let the system recover without manual delay logic.
Microservice Communication: Implement circuit-breaker patterns for inter-service calls where downstream services may be deploying or experiencing temporary resource constraints. Combine with AbortError to fail fast on known unrecoverable errors while retrying network-level failures.
npm install p-retrypnpm add p-retrybun add p-retry