Skip to content

Commit 8c783cd

Browse files
committed
Add retry logic to NPM stats fetch requests
Introduced a fetchWithRetry function to handle network errors and transient server issues when fetching NPM package stats. This improves reliability by retrying failed requests up to three times with a delay.
1 parent 8ff0ca3 commit 8c783cd

File tree

1 file changed

+32
-7
lines changed

1 file changed

+32
-7
lines changed

scripts/collect-npm-stats.ts

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
import { mkdir, readFile, writeFile } from 'node:fs/promises';
1111
import { dirname, join } from 'node:path';
12+
import { setTimeout } from 'node:timers/promises';
1213

1314
interface NpmDownloadResponse {
1415
package: string;
@@ -21,19 +22,43 @@ interface HistoricalData {
2122
downloads: Record<string, number[]>;
2223
}
2324

25+
async function fetchWithRetry(url: string, retries = 3): Promise<Response> {
26+
let response;
27+
28+
try {
29+
response = await fetch(url);
30+
} catch (error) {
31+
// Network error - retry if retries left
32+
if (retries <= 0) {
33+
throw error;
34+
}
35+
}
36+
37+
if (response) {
38+
// Handle successful responses
39+
if (response.ok) {
40+
return response;
41+
}
42+
43+
// Don't retry on 4xx client errors or no retries left
44+
if ((response.status >= 400 && response.status < 500) || retries <= 0) {
45+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
46+
}
47+
}
48+
49+
// Retry after delay
50+
console.log(`Retrying in 1s (${retries} retries left)...`);
51+
await setTimeout(1000);
52+
return fetchWithRetry(url, retries - 1);
53+
}
54+
2455
async function fetchPackageStats(packageName: string): Promise<NpmDownloadResponse> {
2556
const encodedPackage = encodeURIComponent(packageName);
2657
const url = `https://api.npmjs.org/versions/${encodedPackage}/last-week`;
2758

2859
console.log(`Fetching stats for ${packageName}...`);
2960

30-
const response = await fetch(url);
31-
if (!response.ok) {
32-
throw new Error(
33-
`Failed to fetch stats for ${packageName}: ${response.status} ${response.statusText}`,
34-
);
35-
}
36-
61+
const response = await fetchWithRetry(url);
3762
return response.json() as Promise<NpmDownloadResponse>;
3863
}
3964

0 commit comments

Comments
 (0)