phin-retry
raw JSON →phin-retry is an ultra-lightweight Node.js HTTP client that wraps the `phin` library, adding robust retry and delay capabilities. It aims to provide a similar API experience to the now-deprecated `request-promise` library, making it familiar for developers transitioning from older HTTP clients. The current stable version is 2.0.0. While a specific release cadence isn't defined, it typically updates in response to `phin`'s evolution or community needs. Key differentiators include customizable retry strategies (based on network errors, HTTP status codes, or custom logic), configurable exponential or fixed delays between retries, and flexible error handling strategies. It supports standard HTTP methods (GET, POST, PUT, DELETE, PATCH) and exposes all underlying `phin` options, allowing for fine-grained control over requests. Its lightweight nature and focus on resilient API calls make it particularly suitable for microservices or applications requiring dependable external API interactions.
Common errors
error TypeError: request.get is not a function ↓
const request = require('phin-retry'); for CommonJS environments. If using ESM, try import request from 'phin-retry'; and confirm your file is treated as an ES module. Also, ensure await calls are within an async function. error UnhandledPromiseRejectionWarning: Error: Bad status code: 404 ↓
await request(...) call in a try...catch block to handle the thrown error, or provide a custom errorStrategy option in your request configuration to prevent phin-retry from throwing on specific status codes. error ReferenceError: require is not defined ↓
const request = require('phin-retry'); to import request from 'phin-retry';. Additionally, ensure your package.json specifies "type": "module" or that the file ends with a .mjs extension. Warnings
breaking Responses with status codes less than 200 or greater than or equal to 300 are automatically thrown as errors by default, unlike some other HTTP clients (e.g., `request-promise`). ↓
gotcha The documentation primarily uses CommonJS `require()` syntax. While Node.js generally supports both, direct ESM `import` might require specific project configuration or different import paths depending on the package's module resolution. ↓
gotcha By default, `phin-retry` only retries once on network errors or status codes >= 500, with a relatively short delay. This default behavior might not be sufficient for highly unstable external services or specific retry requirements. ↓
Install
npm install phin-retry yarn add phin-retry pnpm add phin-retry Imports
- request wrong
import request from 'phin-retry';correctconst request = require('phin-retry'); - request wrong
const request = require('phin-retry');correctimport request from 'phin-retry'; - phin
const { phin } = require('phin-retry'); // or const request = require('phin-retry'); const phinInstance = request.phin;
Quickstart
const request = require('phin-retry');
const http = require('http'); // For a local server example
// --- Dummy local server for demonstration purposes ---
const server = http.createServer((req, res) => {
if (req.url === '/api/post' && req.method === 'POST') {
let body = '';
req.on('data', chunk => { body += chunk.toString(); });
req.on('end', () => {
console.log('Received POST body:', body);
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ received: JSON.parse(body), status: 'success' }));
});
} else if (req.url === '/api/delete' && req.method === 'DELETE') {
const authHeader = req.headers.authorization;
if (authHeader === 'Basic bmFtZTpzZWNyZXQ=') { // base64 for name:secret
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ status: 'deleted' }));
} else {
res.writeHead(401, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ status: 'unauthorized' }));
}
} else if (req.url === '/error' && req.method === 'GET') {
const failCount = (server.failCount || 0);
if (failCount < 2) {
server.failCount = failCount + 1;
res.writeHead(500, { 'Content-Type': 'text/plain' });
res.end('Internal Server Error (simulated)');
} else {
server.failCount = 0;
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Success after retries!');
}
} else {
res.writeHead(404);
res.end();
}
});
const PORT = 9393;
server.listen(PORT, async () => {
console.log(`Dummy server running on http://localhost:${PORT}`);
try {
// Example 1: Simple GET request
console.log('\n--- Example: Simple GET ---');
const getResponse = await request.get('https://jsonplaceholder.typicode.com/posts/1');
console.log('GET Response Status:', getResponse.statusCode);
console.log('GET Response Body (first 100 chars):', getResponse.body.slice(0, 100));
// Example 2: POST request with retry and delay
console.log('\n--- Example: POST with Retry & Delay ---');
const postResponse = await request.post({
url: `http://localhost:${PORT}/api/post`,
body: { msg: 'input' },
retry: 3,
delay: 500
});
console.log('POST Response Status:', postResponse.statusCode);
console.log('POST Response Body:', JSON.parse(postResponse.body));
// Example 3: DELETE request with custom retry/error/delay strategy
console.log('\n--- Example: DELETE with Custom Strategies ---');
const deleteResponse = await request.delete({
url: `http://localhost:${PORT}/api/delete`,
auth: { user: 'name', pass: 'secret' },
errorStrategy: ({ response, error }) => {
if (error) return true; // Retry on network error
if (response.statusCode >= 400 && response.statusCode !== 401) return false;
return true; // Consider other 4xx codes as retryable
},
retryStrategy: ({ response, error, options }) => {
if (error) return true;
if (options.method === 'DELETE' && response.statusCode === 401) return true;
if (response.statusCode >= 200 && response.statusCode < 300) return false;
return true;
},
delayStrategy: ({ error }) => { if (error) return 5000; return 2000; },
retry: 2
});
console.log('DELETE Response Status:', deleteResponse.statusCode);
console.log('DELETE Response Body:', JSON.parse(deleteResponse.body));
// Example 4: GET with retries due to server error
console.log('\n--- Example: GET with Server Error Retries ---');
const errorRetryResponse = await request.get({
url: `http://localhost:${PORT}/error`,
retry: 3,
delay: 200
});
console.log('Error Retry Response Status:', errorRetryResponse.statusCode);
console.log('Error Retry Response Body:', errorRetryResponse.body);
} catch (err) {
console.error('\nAn error occurred during request examples:', err.message);
if (err.response) {
console.error('Error Response Status:', err.response.statusCode);
console.error('Error Response Body:', err.response.body);
}
} finally {
server.close(() => console.log('\nDummy server closed.'));
}
});