HTTP Nexus
A lightweight HTTP client wrapper for Flutter that handles the tedious parts of API communication. It automatically retries failed requests, manages errors gracefully, and helps you write cleaner networking code.
Why HTTP Nexus?
Working with APIs in Flutter often means writing the same boilerplate code repeatedly: retry logic, timeout handling, error parsing, and logging. This package takes care of all that so you can focus on building features.
Features
Core functionality:
- Automatic retry with exponential backoff
- Clean error types for different failure scenarios
- Request and response interceptors
- Timeout management
- Request cancellation
- Automatic JSON encoding and decoding
Advanced capabilities:
- Offline request queue (stores requests when offline, replays when connected)
- Built-in logging for debugging
- Rate limiting to prevent API abuse
- Global configuration with sensible defaults
- Authentication token management
Installation
Add this to your package's pubspec.yaml file:
dependencies:
http_nexus: ^1.0.0
Then run:
flutter pub get
Quick Start
import 'package:http_nexus/http_nexus.dart';
// Create a client
final client = SmartApiClient(
config: ApiClientConfig(
baseUrl: 'https://api.example.com',
),
);
// Make a GET request
final users = await client.get('/users');
// Make a POST request
final newUser = await client.post(
'/users',
body: {
'name': 'John Doe',
'email': 'john@example.com',
},
);
Basic Usage
Configuration
final config = ApiClientConfig(
baseUrl: 'https://api.example.com',
defaultTimeout: Duration(seconds: 30),
defaultHeaders: {
'Accept': 'application/json',
},
retryPolicy: RetryPolicy(
maxRetries: 3,
initialDelay: Duration(seconds: 1),
backoffMultiplier: 2.0,
),
enableLogging: true,
);
final client = SmartApiClient(config: config);
Making Requests
GET Request
try {
final response = await client.get('/posts/1');
print('Title: ${response['title']}');
} on ServerException catch (e) {
print('Server error: ${e.statusCode}');
} on NetworkException catch (e) {
print('Network error: ${e.message}');
}
POST Request
final newPost = await client.post(
'/posts',
body: {
'title': 'My Post',
'body': 'Post content',
'userId': 1,
},
);
PUT Request
final updated = await client.put(
'/posts/1',
body: {
'title': 'Updated Title',
},
);
DELETE Request
await client.delete('/posts/1');
Query Parameters
final results = await client.get(
'/search',
queryParameters: {
'q': 'flutter',
'limit': 10,
},
);
Custom Headers
final response = await client.get(
'/protected',
headers: {
'Authorization': 'Bearer token123',
},
);
Request Cancellation
// Start request with a cancel token
final cancelToken = 'my-request-1';
final future = client.get('/slow-endpoint', cancelToken: cancelToken);
// Cancel the request
client.cancelRequest(cancelToken);
Using Interceptors
Logging Interceptor
final client = SmartApiClient(config: config);
client.addRequestInterceptor(LoggingInterceptor());
Authentication Interceptor
final authInterceptor = AuthInterceptor(
tokenProvider: () async {
// Return your auth token
return await getAuthToken();
},
);
client.addRequestInterceptor(authInterceptor);
Custom Interceptor
class CustomInterceptor implements RequestInterceptor {
@override
Future<void> onRequest(
Uri url,
HttpMethod method,
Map<String, String> headers,
dynamic body,
) async {
// Add custom logic
headers['X-Custom-Header'] = 'value';
}
}
client.addRequestInterceptor(CustomInterceptor());
Retry Policy
final config = ApiClientConfig(
baseUrl: 'https://api.example.com',
retryPolicy: RetryPolicy(
maxRetries: 5,
initialDelay: Duration(milliseconds: 500),
maxDelay: Duration(seconds: 10),
backoffMultiplier: 2.0,
shouldRetry: (exception, retryCount) {
// Custom retry logic
return exception is NetworkException ||
exception is TimeoutException;
},
),
);
Rate Limiting
final config = ApiClientConfig(
baseUrl: 'https://api.example.com',
maxRateLimitPerMinute: 60, // Max 60 requests per minute
);
Authentication Token Provider
final config = ApiClientConfig(
baseUrl: 'https://api.example.com',
authTokenProvider: () async {
// Fetch token from secure storage
return await SecureStorage.getToken();
},
);
Offline Queue
When enabled, requests made while offline are automatically queued and retried when connection is restored:
final config = ApiClientConfig(
baseUrl: 'https://api.example.com',
enableOfflineQueue: true,
);
Advanced Features
Custom Timeout Per Request
final response = await client.get(
'/slow-endpoint',
timeout: Duration(seconds: 60),
);
Network Status Checking
import 'package:http_nexus/http_nexus.dart';
final networkChecker = NetworkChecker();
// Check if connected
final isConnected = await networkChecker.isConnected();
// Listen to connectivity changes
networkChecker.onConnectivityChanged.listen((status) {
print('Connectivity changed: $status');
});
// Wait for connection
await networkChecker.waitForConnection(
timeout: Duration(seconds: 30),
);
🎯 Error Handling
Smart API Client provides typed exceptions for different error scenarios:
try {
final response = await client.get('/endpoint');
} on NetworkException catch (e) {
// Network connectivity issues
print('Network error: ${e.message}');
} on TimeoutException catch (e) {
// Request timed out
print('Timeout: ${e.message}');
} on ServerException catch (e) {
// Server returned error status code
print('Server error ${e.statusCode}: ${e.message}');
} on AuthenticationException catch (e) {
// 401 - Authentication failed
print('Auth failed: ${e.message}');
} on AuthorizationException catch (e) {
// 403 - Insufficient permissions
print('Access denied: ${e.message}');
} on RateLimitException catch (e) {
// 429 - Rate limit exceeded
print('Rate limit: ${e.message}');
} on ParseException catch (e) {
// Failed to parse response
print('Parse error: ${e.message}');
} on CancelledException catch (e) {
// Request was cancelled
print('Cancelled: ${e.message}');
} on ApiException catch (e) {
// Catch-all for other API errors
print('API error: ${e.message}');
}
API Reference
ApiClientConfig
| Property | Type | Default | Description |
|---|---|---|---|
baseUrl |
String |
required | Base URL for all requests |
defaultTimeout |
Duration |
30s |
Default request timeout |
defaultHeaders |
Map<String, String> |
{} |
Headers added to all requests |
retryPolicy |
RetryPolicy |
RetryPolicy() |
Configuration for retry logic |
enableLogging |
bool |
false |
Enable request/response logging |
enableOfflineQueue |
bool |
true |
Enable offline request queue |
maxRateLimitPerMinute |
int? |
null |
Max requests per minute |
authTokenProvider |
Function? |
null |
Function to provide auth token |
RetryPolicy
| Property | Type | Default | Description |
|---|---|---|---|
maxRetries |
int |
3 |
Maximum retry attempts |
initialDelay |
Duration |
1s |
Initial delay before retry |
maxDelay |
Duration |
30s |
Maximum delay between retries |
backoffMultiplier |
double |
2.0 |
Exponential backoff multiplier |
shouldRetry |
Function |
- | Custom retry decision logic |
SmartApiClient Methods
HTTP Methods
get(endpoint, {headers, queryParameters, timeout, cancelToken})post(endpoint, {body, headers, queryParameters, timeout, cancelToken})put(endpoint, {body, headers, queryParameters, timeout, cancelToken})patch(endpoint, {body, headers, queryParameters, timeout, cancelToken})delete(endpoint, {body, headers, queryParameters, timeout, cancelToken})
Interceptors
addRequestInterceptor(interceptor)addResponseInterceptor(interceptor)
Cancellation
cancelRequest(cancelToken)
Cleanup
close()- Close client and cleanup resources
Best Practices
1. Create a single client instance
Avoid creating multiple client instances. Instead, create one and reuse it throughout your app:
// Good: Singleton instance
class ApiService {
static final ApiService _instance = ApiService._internal();
factory ApiService() => _instance;
late final SmartApiClient client;
ApiService._internal() {
client = SmartApiClient(config: ApiClientConfig(
baseUrl: 'https://api.example.com',
));
}
}
// Usage
final api = ApiService().client;
2. Use specific error types
Handle errors specifically rather than catching all exceptions:
// Good: Handle specific errors differently
try {
await client.get('/data');
} on AuthenticationException {
navigateToLogin();
} on NetworkException {
showOfflineBanner();
}
3. Configure timeouts appropriately
Different endpoints may need different timeouts:
// Good: Adjust timeout based on expected response time
await client.get('/fast', timeout: Duration(seconds: 5));
await client.post('/slow-upload', timeout: Duration(minutes: 2));
4. Always clean up resources
Close the client when you're done with it:
@override
void dispose() {
client.close();
super.dispose();
}
Common Use Cases
File Upload (with JSON)
final imageBase64 = base64Encode(imageBytes);
await client.post('/upload', body: {
'filename': 'photo.jpg',
'data': imageBase64,
});
Pagination
Future<List<Post>> loadPage(int page) async {
final response = await client.get(
'/posts',
queryParameters: {
'page': page,
'limit': 20,
},
);
return (response as List).map((json) => Post.fromJson(json)).toList();
}
Authentication Flow
Future<void> login(String email, String password) async {
final response = await client.post('/auth/login', body: {
'email': email,
'password': password,
});
await saveToken(response['token']);
}
Testing
The package includes comprehensive unit tests. Run them with:
flutter test
Example App
Check out the example app in the /example folder for a complete working demonstration of all features.
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
This project is licensed under the MIT License - see the LICENSE file for details.
Support
If you find this package helpful, please consider giving it a star on GitHub. If you encounter any issues or have questions, please open an issue on the repository.
Changelog
See CHANGELOG.md for a list of changes in each version.
Built with Flutter
Libraries
- client/smart_api_client
- core/api_exception
- core/config
- core/retry_policy
- http_nexus
- A powerful, lightweight HTTP client wrapper for Flutter with automatic retries, error handling, and network failure management.
- interceptors/auth_interceptor
- interceptors/logging_interceptor
- interceptors/request_interceptor
- interceptors/response_interceptor
- utils/network_checker
- utils/offline_queue
- utils/rate_limiter