http_nexus 1.0.0
http_nexus: ^1.0.0 copied to clipboard
A lightweight but powerful HTTP client wrapper that simplifies API handling with automatic retries, error management, and network failure handling.
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