-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathapi.js
82 lines (66 loc) · 2.52 KB
/
api.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
// Rate limiting configuration
const rateLimiter = {
tokens: 5,
refillRate: 1000, // 1 token per second
lastRefill: Date.now(),
bucket: 5, // Start with full bucket
};
// Token bucket rate limiting implementation
function checkRateLimit() {
const now = Date.now();
const timePassed = now - rateLimiter.lastRefill;
const refillAmount = Math.floor(timePassed / rateLimiter.refillRate);
if (refillAmount > 0) {
rateLimiter.bucket = Math.min(rateLimiter.tokens, rateLimiter.bucket + refillAmount);
rateLimiter.lastRefill = now;
}
if (rateLimiter.bucket <= 0) {
throw new Error('Rate limit exceeded. Please try again later.');
}
rateLimiter.bucket--;
}
export async function fetchCountryStats(countryName) {
// Input validation
if (!countryName || typeof countryName !== 'string') {
throw new Error('Invalid country name');
}
// Check rate limit before making request
checkRateLimit();
// Sanitize input - only allow letters, numbers, spaces and hyphens
if (!/^[a-zA-Z0-9\s-]+$/.test(countryName)) {
throw new Error('Invalid country name format');
}
const apiUrl = `https://restcountries.com/v3.1/name/${encodeURIComponent(countryName)}?fullText=true`;
try {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 5000); // 5 second timeout
const response = await fetch(apiUrl, {
signal: controller.signal,
headers: {
'Accept': 'application/json'
},
referrerPolicy: 'no-referrer'
});
clearTimeout(timeout);
if (!response.ok) {
throw new Error(`Failed to fetch data for ${countryName}: ${response.statusText}`);
}
const jsonData = await response.json();
// Validate response structure
if (!Array.isArray(jsonData) || !jsonData.length || !jsonData[0]) {
throw new Error('Invalid response format');
}
// Basic data sanitization
const sanitizedData = jsonData[0];
if (typeof sanitizedData !== 'object') {
throw new Error('Invalid data format');
}
return sanitizedData;
} catch (error) {
console.error("Error fetching country stats:", error);
if (error.name === 'AbortError') {
throw new Error('Request timeout');
}
return null;
}
}