@@ -5,6 +5,39 @@ import StatusCodes from 'http-status-codes';
55
66export const weatherRoute = Router ( ) ;
77
8+ // Helper function for retry logic with exponential backoff
9+ const retryWithBackoff = async < T > (
10+ fn : ( ) => Promise < T > ,
11+ maxRetries : number = 3 ,
12+ baseDelay : number = 1000
13+ ) : Promise < T > => {
14+ let lastError : Error | unknown ;
15+
16+ for ( let attempt = 0 ; attempt <= maxRetries ; attempt ++ ) {
17+ try {
18+ return await fn ( ) ;
19+ } catch ( error ) {
20+ lastError = error ;
21+
22+ // Don't retry on client errors (4xx) or if it's the last attempt
23+ if ( axios . isAxiosError ( error ) && error . response ?. status && error . response . status < 500 ) {
24+ throw error ;
25+ }
26+
27+ if ( attempt === maxRetries ) {
28+ throw error ;
29+ }
30+
31+ // Exponential backoff: 1s, 2s, 4s
32+ const delay = baseDelay * Math . pow ( 2 , attempt ) ;
33+ console . log ( `Weather API attempt ${ attempt + 1 } failed, retrying in ${ delay } ms...` ) ;
34+ await new Promise ( resolve => setTimeout ( resolve , delay ) ) ;
35+ }
36+ }
37+
38+ throw lastError ;
39+ } ;
40+
841/**
942 * GET /weather
1043 * Requires query parameters `latitude` and `longitude`.
@@ -22,17 +55,19 @@ weatherRoute.get('/', async (req: Request, res: Response): Promise<void> => {
2255 const latitude = req . query . latitude ;
2356 const longitude = req . query . longitude ;
2457
25- // Fetch weather data from Open-Meteo with timeout
26- const weatherResponse = await axios . get ( 'https://api.open-meteo.com/v1/forecast' , {
27- params : {
28- latitude : latitude ,
29- longitude : longitude ,
30- current : 'temperature_2m,weathercode,windspeed_10m' ,
31- daily : 'temperature_2m_max,temperature_2m_min,weathercode,sunrise,sunset' ,
32- timezone : 'auto'
33- } ,
34- timeout : 5000 // 5 second timeout
35- } ) ;
58+ // Fetch weather data from Open-Meteo with retry logic
59+ const weatherResponse = await retryWithBackoff ( async ( ) => {
60+ return await axios . get ( 'https://api.open-meteo.com/v1/forecast' , {
61+ params : {
62+ latitude : latitude ,
63+ longitude : longitude ,
64+ current : 'temperature_2m,weathercode,windspeed_10m' ,
65+ daily : 'temperature_2m_max,temperature_2m_min,weathercode,sunrise,sunset' ,
66+ timezone : 'auto'
67+ } ,
68+ timeout : 5000 // 5 second timeout
69+ } ) ;
70+ } , 3 , 1000 ) ; // 3 retries with 1 second base delay
3671
3772 res . json ( weatherResponse . data ) ;
3873
@@ -43,20 +78,20 @@ weatherRoute.get('/', async (req: Request, res: Response): Promise<void> => {
4378 if ( axios . isAxiosError ( error ) ) {
4479 if ( error . code === 'ECONNABORTED' ) {
4580 statusCode = StatusCodes . GATEWAY_TIMEOUT ;
46- errorMessage = 'Weather API timeout' ;
81+ errorMessage = 'Weather API timeout after retries ' ;
4782 } else if ( error . response ) {
4883 statusCode = error . response . status ;
4984 errorMessage = `Weather API error: ${ error . response . statusText } ` ;
5085 }
5186
52- console . error ( `Weather API error: ${ errorMessage } ` , {
87+ console . error ( `Weather API error after retries : ${ errorMessage } ` , {
5388 status : statusCode ,
5489 message : error . message ,
5590 url : error . config ?. url ,
5691 params : error . config ?. params
5792 } ) ;
5893 } else {
59- console . error ( 'Unknown error fetching weather:' , error ) ;
94+ console . error ( 'Unknown error fetching weather after retries :' , error ) ;
6095 }
6196
6297 res . status ( statusCode ) . json ( { error : errorMessage } ) ;
0 commit comments