makeRequest method
Centralized request with retry logic and exponential backoff.
Implementation
Future<http.Response> makeRequest(
String method,
Uri url, {
Map<String, String>? headers,
dynamic body,
bool useAuth = true,
bool isAuthRetry = false,
int maxRetries = 3,
int retryCount = 0,
}) async {
// Clone so the Authorization injection never mutates the caller's map
// (it would otherwise leak a stale bearer token across reuses/retries).
final requestHeaders = headers != null
? Map<String, String>.from(headers)
: <String, String>{'Content-Type': 'application/json'};
if (useAuth && apiKey != null && apiKey!.isNotEmpty) {
requestHeaders['Authorization'] = 'Bearer $apiKey';
}
http.Response response;
try {
switch (method.toUpperCase()) {
case 'GET':
response = await _client.get(url, headers: requestHeaders);
break;
case 'POST':
response =
await _client.post(url, headers: requestHeaders, body: body);
break;
case 'PUT':
response =
await _client.put(url, headers: requestHeaders, body: body);
break;
case 'PATCH':
response =
await _client.patch(url, headers: requestHeaders, body: body);
break;
case 'DELETE':
final request = http.Request('DELETE', url)
..headers.addAll(requestHeaders)
..body = body ?? '';
final streamedResponse = await _client.send(request);
response = await http.Response.fromStream(streamedResponse);
break;
default:
throw Exception('Unsupported HTTP method: $method');
}
} catch (e) {
log('Network error: $e');
if (retryCount < maxRetries) {
final delay = Duration(seconds: 1 << retryCount);
log('Retrying in ${delay.inSeconds}s... (${retryCount + 1}/$maxRetries)');
await Future.delayed(delay);
return makeRequest(
method,
url,
headers: headers,
body: body,
useAuth: useAuth,
isAuthRetry: isAuthRetry,
maxRetries: maxRetries,
retryCount: retryCount + 1,
);
}
throw Exception('Network request failed after $maxRetries attempts: $e');
}
// Handle 5xx errors with retry
if (response.statusCode >= 500 && response.statusCode < 600) {
if (retryCount < maxRetries) {
final delay = Duration(seconds: 1 << retryCount);
log('Server error ${response.statusCode}. Retrying in ${delay.inSeconds}s...');
await Future.delayed(delay);
return makeRequest(
method,
url,
headers: headers,
body: body,
useAuth: useAuth,
isAuthRetry: isAuthRetry,
maxRetries: maxRetries,
retryCount: retryCount + 1,
);
}
}
// Handle 401
if (response.statusCode == 401 && useAuth && !isAuthRetry) {
log('Auth error (401). API key may be invalid.');
}
if (response.statusCode < 200 || response.statusCode >= 300) {
log('API Error ${response.statusCode}: ${response.body}');
throw Exception('API Error: ${response.statusCode} - ${response.body}');
}
return response;
}