baaba_api_handler
A typed, functional HTTP client for Flutter. Wraps Dio with Either-based error handling, token refresh, retries, response caching, and a built-in test double.
2.0.0
What this package is like to depend on
Last release 7 days ago
17 Aug 2026
Release timing varies
gaps range from 1 weeks to 2 months
Nearly every release is documented
notes for 13 of 13 stable releases
Nothing withdrawn
no release was ever pulled
5 months old
13 releases · first in 2026
13 releases in the last 12 months
see the full history below
Release timeline
13 releases · Mar 2026 to Aug 2026Releases
latest 13-
2.0.017 Aug 2026Release notes
Open source →Everything from 1.x keeps working — the old entrypoints are deprecated, not removed. See
MIGRATION.mdfor the three things that can actually break you.⚠️ Behaviour changes to know about before upgrading
- Requests now time out. 1.x set no timeout at any layer, so a request to an unreachable-but-not-refusing host hung until the OS gave up. The defaults are 30s each for connect/receive/send. If you have an endpoint that legitimately takes longer — a slow export or report — raise
receiveTimeoutfor that call or globally, rather than removing the bound. ErrorSourceandResponseCodegained aparseErrorvariant. If youswitchexhaustively over either enum, that switch no longer compiles until you add a case. This is the only source-breaking change and the reason this is a major release.Failureequality now includesdataandstatusCode. Two failures that differ only in response body are no longer equal.
Fixed
ApiCacheHelper.getCacheDatathrew on a cache miss. The underlyingAPICacheManager.getCacheDatacalls.firston an empty query result, so asking for a URL that was never cached raisedStateErrordespite the nullable return type promising otherwise. It now returnsnull.- Concurrent writes to one cache key could poison it permanently.
APICacheManager.addCacheDatachecksisAPICacheKeyExistand then inserts or updates, with no transaction around the pair, so two overlapping writes both insert. The duplicate rows are not self-correcting:isAPICacheKeyExistanswersrows.length == 1, so it then reports the key as missing forever while every further write appends another row. Request de-duplication makes overlapping same-key writes routine rather than rare, sosetCacheDatanow serialises writes per key. - Uploads threw instead of retrying. A
FormDatais a stream Dio reads once —finalize()throwsStateErroron a second read. Both replay paths hit it: a429/503retry (those bypass the idempotency check, so an upload is retried) and a401token refresh, which is a likely outcome for an upload long enough to outlive its token. The body is now rebuilt withFormData.clone()before either replay. - A JSON
content-typein caller-supplied headers broke multipart uploads. It was only stripped from the package's default headers, so an upload that also needed, say, anX-Tenant-Idheader silently sentapplication/jsonand lost the multipart boundary. - A connectivity probe that threw synchronously disabled the check permanently. An
asyncbody runs synchronously to its firstawait, so such a probe completed — and cleared the in-flight slot — before that slot was assigned, stranding a completed future that reported offline for the rest of the session. - Cached entries with query parameters collided. The cache key was the raw url, so
/userswithparams: {'page': 1}andpage: 2shared one entry and the second overwrote the first. Query parameters are now part of the key, sorted so argument order does not matter. - The connectivity probe ran before every single request — a real network round-trip that roughly doubled the latency of a fast API call. A positive result is now reused for
connectivityCacheTtl(default 5s). Negative results are deliberately never cached, since that is exactly when the user is retrying. Failurediscarded the response body, making422field errors unreachable.- Interceptor order was assembled across two call sites and did not match the documented order. It is now fixed in one place, with auth before retry so a retried request carries a valid token.
DioExceptionType.badCertificatemapped to the generic "unexpected error" instead ofconnectionFailure.
Added
ApiConfig+ApiServices.init(...)— one object for every setting, replacingconfigure()and the loose static setters. AddsbaseUrl(so call sites pass/users, not the full URL),connectTimeout/receiveTimeout/sendTimeout, anddefaultHeaders.- Typed responses —
getAs<T>,postAs<T>,putAs<T>,patchAs<T>,deleteAs<T>take aparserand returnEither<Failure, T>. A throwing parser becomes aFailurewithErrorSource.parseErrorcarrying the raw body; no exception escapes.listParser(User.fromJson)handles list endpoints, with an optionalkeyfor{"data": [...]}wrappers. - Response caching —
cachePolicyandcacheMaxAgeonget/getAs, withCachePolicy.cacheFirst,networkFirst,cacheOnly, and the defaultnetworkOnly(unchanged behaviour).response.isFromCachetells you which you got.ApiCacheHelperwas already in the package but nothing called it. - Project-level cache control —
ApiConfig.cacheEnabled: falseforbids caching outright, overriding anycachePolicya call site passes and never opening the database;ApiConfig.defaultCachePolicysets the policy for calls that don't name one. Not every app should cache, and "just don't pass a policy" relies on every call site getting it right. ApiLogOptions.trimBase64— collapses base64 blobs in log output. An API returning photographs or fingerprints inline turns a single response into thousands of console lines, because the logger wraps every value atmaxWidthand has no notion of a field worth hiding. With this on, a blob prints as a recognisable head plus a count of what was elided, and everything else passes through byte for byte. Off by default.Base64LogTrimmeris exported for tuningminRunLength/keptCharsor placing the trimmer in front of your own sink.- In-flight de-duplication — two identical GETs at the same time share one network call. Skipped automatically when you pass your own
CancelToken, since cancelling one caller must not cancel the other; opt out withdedupe: false. RetryPolicy— configurablemaxRetries,baseDelay,maxDelay,retryableStatusCodes, and aretryIfpredicate. Retries now cover status codes as well as transport errors:429and503for any method (the server told us it did not process the request),408/500/502/504for idempotent methods only.Retry-Afteris honoured in both its delta-seconds and HTTP-date forms. Backoff is exponential with full jitter, replacing the lockstep linear interval that made concurrent failures retry in unison.ApiConfig.isSuccess— treat a200carrying{"success": false}as a failure, with the message pulled from the body the same way a real error response would be.ApiObserver— one hook for every request, response, and failure, for Sentry/Crashlytics/analytics. Fires exactly once per call, including for failures Dio never produces an exception for (offline short-circuits, parse errors,isSuccessrejections). A throwing observer can never break a request.ApiConfig.httpClientAdapter— supply your own adapter for certificate pinning or to route through Charles/Proxyman. Deliberately consumer-supplied so the package stays usable on web.upload()— multipart uploads with progress, takingUploadFile.fromPath(mobile/desktop) orUploadFile.fromBytes(web, where a picked file has no path). The multipart body is rebuilt before any replay — a429/503retry or a401token refresh — because aFormDatais a stream that Dio reads once and refuses to read again. A JSONcontent-typeis stripped for multipart bodies, including one you pass yourself.ApiServices.reset()— clears the singleton, config, and loader callbacks. FixessetLoggingbeing silently a no-op after the firstinstance()call, and gives tests a clean slate.package:baaba_api_handler/testing.dart— shipsFakeApiServices, an in-memory double with stubbing and call recording, so consumers can test repositories without mocking Dio. An unstubbed endpoint throws aStateErrornaming it rather than quietly returning null.Failure.data,Failure.statusCode,Failure.validationErrors,Failure.requestOptions— the raw body, the literal HTTP status (codecollapses anything unrecognised todefaultError), per-field errors parsed from a{"errors": {...}}body, and the request that failed.requestOptionsis excluded from equality: it is context about where a failure came from, not part of what the failure is.package:baaba_api_handler/baaba_api_handler.dartas the conventional entrypoint. The oldts_api_handler.dartimport still works.
- Requests now time out. 1.x set no timeout at any layer, so a request to an unreachable-but-not-refusing host hung until the OS gave up. The defaults are 30s each for connect/receive/send. If you have an endpoint that legitimately takes longer — a slow export or report — raise
-
1.4.010 Aug 2026Release notes
Open source →- Added
ApiLogOptions— the consuming app now controls what the console logger prints:enabled,request,requestHeader,requestBody,responseHeader,responseBody,error,maxWidth,compact, andlogPrint. Previously the logger was hardcoded torequestBody: truewith no way to change it. Includes anApiLogOptions.disabled()constructor andcopyWith. - Added
loggingparameter toApiServices.configure(), defaulting toconst ApiLogOptions()— same output as before, so existing callers see no change. - Added
ApiServices.setLogging(ApiLogOptions)— same control for apps that don't use token auth. Must be called before the firstApiServices.instance(), since the Dio client and its logger are built once and cached. - Release builds are unaffected: the logger is still never attached when
kReleaseModeis true.
- Added
-
1.3.106 Aug 2026Release notes
Open source →- Added support for the
detailkey in API error responses (RFC 7807), falling back to it ifmessageis missing but prioritizing it overerror.
- Added support for the
-
1.3.014 Jul 2026Release notes
Open source →- Added
ApiServices.download()— streams a file response directly to disk (savePath) instead of loading it into memory, withonReceiveProgress,cancelToken, anddeleteOnErrorsupport. Goes through the same connectivity check and loader plumbing as the other HTTP methods. - Added
refreshTimeoutparameter toApiServices.configure()(default 30 seconds). Bounds how long a request that 401s while another refresh is already in flight will wait for that refresh before giving up and failing with the original error. TokenRefreshInterceptor: requests that 401 while a refresh is already in progress now wait for that refresh to finish and retry with the fresh token, instead of failing immediately. Previously only the first request in a concurrent-401 burst would succeed; the rest errored out just for losing the race.NetworkRetryInterceptornow only auto-retries idempotent methods (GET,HEAD,OPTIONS,PUT,DELETE).POST/PATCHare no longer retried on transient timeouts/connection errors, since the server may have already processed the request and a blind retry could duplicate the side effect.- Added
maxAgeparameter toApiCacheHelper.getCacheData()— if the cached entry is older thanmaxAge, it's treated as a miss (the stale entry is cleared andnullreturned) instead of returning stale data. Omit it to keep the previous behaviour of returning cached data regardless of age. - Bumped
dioto^5.10.0,pretty_dio_loggerto^1.4.0,internet_connection_checker_plusto^3.1.1,fpdartto^1.2.0,equatableto^2.1.0, andsqflite_common_ffi(dev) to^2.4.0+3.
- Added
-
1.2.003 Jul 2026Release notes
Open source →- Added
ApiServices.configureLoader({onShow, onHide})— a global, framework-agnostic loading indicator shown automatically around every request (success, failure, and thrown exceptions all covered), so callers no longer need a per-screenisLoadingflag.onShow/onHideare plain callbacks (e.g.Get.dialog/Get.backfor GetX, orshowDialog/Navigator.popwith a global key) — the package has no UI dependency. - Added
showLoaderparameter (defaulttrue) toget/post/put/patch/deleteto opt a specific request out of the loader. - Concurrent requests share one indicator via reference counting:
onShowfires only for the first in-flight request,onHideonly once every in-flight request has finished.
- Added
-
1.1.030 Jun 2026Release notes
Open source →- Breaking:
ErrorSourceenum variants renamed fromsnake_casetocamelCase(e.g.no_content→noContent,bad_request→badRequest). Update anyswitchor direct references in your code. - Added
bypassConnectivityCheckparameter toApiServices.configure()for staging/internal environments where connectivity probes fail due to proxies or firewalls. - Added
ApiServices.setConnectivityCheck({bool enabled})— controls the connectivity check independently of token auth configuration. cancelRequest()now cancels all in-flight requests (previously only the most recent). All activeCancelTokens are tracked in aSetand cancelled together.- Extended
ResponseCodeandErrorSourcewith six new HTTP status codes:created(201),requestTimeout(408),conflict(409),unprocessableEntity(422),tooManyRequests(429),badGateway(502). - Fixed
ResponseCode.noContentraw value from 201 to 204. ResponseCoderefactored to use inline integer values (ResponseCode.success(200)style) — no longer requires an extension for.value.ResponseStringsrewritten with cleaner, user-facing error messages.ErrorHandlerno longer implementsException.
- Breaking:
-
1.0.724 Apr 2026Release notes
Open source →- Added
TokenRefreshInterceptorfor automatic token refresh on 401 responses. - Added
NetworkRetryInterceptorfor automatic retry on transient network failures. - Added
ApiServices.configure()static method for setting up token-based authentication. - Added
headerBuilderparameter toApiServices.configure()for customising auth headers per request. - Added
onSendProgress,onReceiveProgress, andCancelTokenparameters to all request methods. - Re-exported
ResponseandCancelTokenfrom Dio, andAPICacheDBModelfrom api_cache_manager — no separate imports needed.
- Added
-
1.0.509 Apr 2026 -
1.0.425 Mar 2026 -
1.0.325 Mar 2026 -
1.0.225 Mar 2026Release notes
Open source →- Fixed multipart request method naming.
- Fixed incorrect MIME types in multipart requests.
-
1.0.125 Mar 2026 -
1.0.025 Mar 2026Release notes
Open source →- Initial release.
- HTTP methods: GET, POST, PUT, PATCH, DELETE.
- API response caching via
ApiCacheHelper. - Network connectivity checks before each request.
- Structured error handling with
Failure,ErrorSource, andResponseCode.