api_request_handler 0.1.0
api_request_handler: ^0.1.0 copied to clipboard
A package for handling API requests with Dio with cache functionality and auto cancel duplicate requests.
API Service #
A lightweight and efficient Dart/Flutter package for handling API requests with optional response caching. Ideal for apps that need consistent, streamlined API access with performance optimization via local cache storage.
β¨ Features #
- Simple API request handling using Dio
- Built-in request caching support (Hive-backed, per-request TTL or a package-wide default)
- Automatic cancellation of a superseded duplicate in-flight request
- Support for GET, POST, PUT, PATCH, and DELETE
- Upload/download progress callbacks
- Optional automatic retry with backoff for transient network errors
- Optional request-level logging
- Typed responses via
requestTyped<T>() - A
requestPaginated<T>()helper for list endpoints - A reusable
AuthInterceptorbase class for token attach + refresh-on-401 - Support for multiple API clients (different base URLs per service)
- Third-party API request support with isolated base logic
π Getting started #
Prerequisites #
- Flutter 3.10+
- Add
api_request_handlerto yourpubspec.yaml:
dependencies:
api_request_handler:
path: ../path_to/api_request_handler
Initialization #
await ApiRequest().initialize(
baseUrl: 'https://api.example.com',
enableLogging: true,
defaultCacheDuration: const Duration(minutes: 5), // omit to make caching opt-in per call
connectTimeout: const Duration(seconds: 10),
receiveTimeout: const Duration(seconds: 10),
);
// For multiple clients
await ApiRequest().initializeMultipleClients(
baseUrls: {
'consumer': 'https://consumer.example.com',
'cas': 'https://cas.example.com',
},
globalHeaders: {
'Authorization': 'Bearer token',
},
interceptors: [TokenInterceptor()],
);
π¦ Usage #
// Using a named client
final response = await ApiRequest().request(
endpoint: '/users',
method: RequestMethod.get,
clientName: 'consumer',
cacheDuration: const Duration(minutes: 5), // omit to skip caching for this call
);
// Using a third-party base URL
final thirdPartyResponse = await ApiRequest().request(
endpoint: '/third-party-endpoint',
method: RequestMethod.post,
data: {'key': 'value'},
isThirdParty: true,
thirdPartyBaseUrl: 'https://thirdparty.com',
);
A successful call returns the parsed response body (Map<String, dynamic> / List<dynamic> / primitive). A failed call returns a normalized error map: {'status_code', 'message', 'data', 'success': false}.
Typed responses #
final user = await ApiRequest().requestTyped<User>(
endpoint: '/users/me',
method: RequestMethod.get,
fromJson: (json) => User.fromJson(json),
);
Pagination #
Each page/param combination is cached under its own key, so pages never collide or get merged.
final result = await ApiRequest().requestPaginated<Item>(
endpoint: '/items',
page: 1,
pageSize: 20,
itemsKey: 'data', // key holding the list in the response body
totalKey: 'total', // optional; used to compute hasMore
fromJson: (json) => Item.fromJson(json),
);
// result.items, result.hasMore, result.totalItems
To invalidate every cached page of one endpoint at once:
await ApiRequest().clearCacheForEndpoint('/items');
Cache control #
ApiRequest().clearCache('/users'); // one specific cached response
await ApiRequest().clearCacheForEndpoint('/users'); // every cached page/param variant of an endpoint
await ApiRequest().clearAllCache(); // wipe the entire cache
Retries #
ApiRequest().configureRetries(retries: 2, retryDelay: const Duration(milliseconds: 500));
// or per-call:
await ApiRequest().request(endpoint: '/flaky', method: RequestMethod.get, retries: 3);
Retries apply only to transient network errors (timeouts, connection errors) β not to 4xx/5xx HTTP responses.
Connectivity check #
ApiRequest().isConnectedChecker = () async {
// e.g. backed by the connectivity_plus package in your app
return await hasNetworkConnection();
};
Auth / token refresh #
class MyAuthInterceptor extends AuthInterceptor {
MyAuthInterceptor() : super(dio: () => ApiRequest().getNamedDio('consumer'));
@override
Future<String?> getAccessToken() => TokenStore.readAccessToken();
@override
Future<bool> refreshToken() => TokenStore.refresh();
@override
Future<void> onRefreshFailed() => TokenStore.logout();
}
Pass an instance via interceptors: [MyAuthInterceptor()] on initialize/initializeMultipleClients.
π Additional information #
- This package uses
Hivefor caching, stored in the platform's cache directory. - Cache keys account for endpoint, HTTP method, client, and (order-independent) query params, so identical params in a different order still hit the cache, and the same endpoint on different clients/methods never collides.
- Issues and contributions are welcome.
- For suggestions or help, reach out to the package maintainer.